Java 模拟假设存在的私有变量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19521220/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 17:59:42  来源:igfitidea点击:

Mocking a Private Variable that is Assumed to Exist

javamockingmockitopowermock

提问by Walls

How can you get a mock object in at runtime when it is not created/initialized in the class you are testing, it is not static (singleton pattern), or you don't have some sort of test constructor to hook into?

如果模拟对象不是在您正在测试的类中创建/初始化,它不是静态的(单例模式),或者您没有某种类型的测试构造函数可以挂钩,您如何在运行时获取模拟对象?

In a class that I am writing some unit testing for, I have come across a scenario I haven't encountered/solved yet. I have a JMS resource (a QueueConnectionFactoryfor reference, but it shouldn't matter), that is a private variable of the class I am testing. Since it has the javax.annotation.Resourceannotation, at runtime it is assumed to be available. During testing, it is not, which creates the need for mocking this object.

在我正在为其编写一些单元测试的课程中,我遇到了一个我还没有遇到/解决的场景。我有一个 JMS 资源(QueueConnectionFactory供参考,但它不重要),这是我正在测试的类的私有变量。由于它具有javax.annotation.Resource注释,因此在运行时假定它可用。在测试期间,它不是,这就需要模拟这个对象。

It is not a static class and is not being used in a static way, if it was I could easily mock using the various static mocking methods I have run into. Since the resource is never created locally (in a constructor or even in a test constructor), I have no way of passing in a Mock object so that at runtime of the test, the mock is used instead of the actual object. How can I mock this Resource so that when the test executes, it will be used in place of the private @Resourceobject in the class I am testing?

它不是静态类,也不是以静态方式使用,如果是的话,我可以使用我遇到的各种静态模拟方法轻松模拟。由于资源从未在本地创建(在构造函数中甚至在测试构造函数中),因此我无法传入 Mock 对象,以便在测试运行时使用模拟而不是实际对象。我如何模拟这个资源,以便在测试执行时,它将被用来代替@Resource我正在测试的类中的私有对象?

For reference, the code is calling createConnection()on the QueueConnectionFactorywhich is throwing a null pointer exception since the Factory has not been initialized/mocked.

作为参考,代码调用createConnection()QueueConnectionFactory其中抛出一个空指针异常,因为该工厂尚未初始化/嘲笑。

@Stateless
public class Example{
  @Resource(name = "jms/exampleQCF")
  private QueueConnectionFactory queueFactory;

  ...

  public void testMe(){
    Connection connection = queueFactory.createConnection();
    ...
  }
}

采纳答案by Walls

After a lot more hunting around and looking at all the options Mockito/Powermock had to offer, I found the solution (which I will share in case others run into this same issue).

在四处寻找并查看 Mockito/Powermock 必须提供的所有选项之后,我找到了解决方案(如果其他人遇到同样的问题,我将分享)。

When you have private member variables that are never initialized (and just assumed created in other places), you can use the @InjectMocksannotation to "inject" Mocks you want into your class you are testing.

当您有从未初始化的私有成员变量(并且只是假设在其他地方创建)时,您可以使用@InjectMocks注释将您想要的 Mocks“注入”到您正在测试的类中。

  1. Add a variable in your test class for the class you are testing, and give it the annotation @InjectMocks(org.Mockito.InjectMocks).
  2. Use @Mockannotations to setup the mocks you want to inject. Use the @Mock (name = "privateVariableNameHere")name property to map the Mock object to the private variable inside your class you are testing.
  3. In either a setup function or before you call your class, initialize the mocks. The easiest way I have found is to use a "setup" method with the @Beforeannotation. Then inside there call MockitoAnnotations.initMocks(this);to quickly initialize anything with the @Mockannotation.
  4. Define your Mock functionality in your test method (before calling the method you are testing).
  5. Using the @InjectMockobject, call your method you are testing... the mocks SHOULD be hooked in and working as defined in the earlier steps.
  1. 在测试类中为您正在测试的类添加一个变量,并为其添加注释@InjectMocks(org.Mockito.InjectMocks)。
  2. 使用@Mock注释来设置要注入的模拟。使用@Mock (name = "privateVariableNameHere")name 属性将 Mock 对象映射到您正在测试的类中的私有变量。
  3. 在设置函数中或在调用类之前,初始化模拟。我发现的最简单的方法是使用带有@Before注释的“设置”方法。然后在里面调用MockitoAnnotations.initMocks(this);以使用@Mock注释快速初始化任何内容。
  4. 在您的测试方法中定义您的 Mock 功能(在调用您正在测试的方法之前)。
  5. 使用该@InjectMock对象,调用您正在测试的方法......模拟应该被挂钩并按照前面步骤中的定义工作。

So for the example class I use above, the code to test/mock would have Connectionreturned as a mock which you can do whatever with. Based on the example above in my question, this is what the code would look like:

所以对于我上面使用的示例类,测试/模拟的代码将Connection作为模拟返回,你可以用它做任何事情。基于我问题中的上述示例,代码如下所示:

@RunWith(PowerMockRunner.class)
@PrepareForTest({/* Static Classes I am Mocking */})
public class ExampleTest {
  @Mock (name = "queueFactory") //same name as private var.
  QueueConnectionFactory queueFactoryMock;
  @Mock
  Connection connectionMock; //the object we want returned
  @InjectMocks
  Example exampleTester; //the class to test

  @Before
  public void setup(){
    MockitoAnnotations.initMocks(this); // initialize all the @Mock objects
    // Setup other Static Mocks
  }

  @Test
  public void testTestMe(){
    //Mock your objects like other "normally" mocked objects
    PowerMockito.when(queueFactoryMock.createConnection()).thenReturn(connectionMock);
    //...Mock ConnectionMock functionality...
    exampleTester.testMe();
  }
}

回答by WesternGun

Several approaches here:

这里有几种方法:

  1. ReflectionTestUtilsof Spring Testing framework: ReflectionTestUtils.setField(objectToTest, "privateFieldName", mockObjectToInject);. With this you don't introduce another dependency.
  2. org.mockito.internal.util.reflection.FieldSetter.
  3. PowerMock.Whitebox.setInternalState()to mock a private field.
  1. ReflectionTestUtilsSpring 测试框架:ReflectionTestUtils.setField(objectToTest, "privateFieldName", mockObjectToInject);. 有了这个,您就不会引入另一个依赖项。
  2. org.mockito.internal.util.reflection.FieldSetter.
  3. PowerMock.Whitebox.setInternalState()模拟私有字段。

If you need to mock internal local variable creation, use PowerMockito.whenNew(Foo.class).withNoArguments().thenReturn(foo);. Very, very useful. Cannot find other ways to do the same.

如果您需要模拟内部局部变量的创建,请使用PowerMockito.whenNew(Foo.class).withNoArguments().thenReturn(foo);. 非常,非常有用。找不到其他方法来做同样的事情。

With only Mockito you cannot mock local variable creation, because when(any(Foo.class)does not work; will return null. It compiles but does not work.

只有 Mockito 不能模拟局部变量的创建,因为when(any(Foo.class)它不起作用;将返回空值。它编译但不起作用。

References: Mockito: Mock private field initialization

参考资料: Mockito:模拟私有字段初始化