模拟假定存在的私有变量

2022-09-02 00:47:58

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

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

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

作为参考,代码正在调用 哪个正在引发空指针异常,因为工厂尚未初始化/模拟。createConnection()QueueConnectionFactory

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

  ...

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

答案 1

经过更多的搜索并查看了Mockito / Powermock提供的所有选项,我找到了解决方案(如果其他人遇到同样的问题,我将分享)。

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

  1. 在测试类中为要测试的类添加一个变量,并为其提供注释 (org.Mockito.InjectMocks)。@InjectMocks
  2. 使用注释来设置要注入的模拟。使用 name 属性将 Mock 对象映射到要测试的类中的私有变量。@Mock@Mock (name = "privateVariableNameHere")
  3. 在设置函数中或在调用类之前,初始化模拟。我发现的最简单的方法是对注释使用“setup”方法。然后在内部调用以使用注释快速初始化任何内容。@BeforeMockitoAnnotations.initMocks(this);@Mock
  4. 在测试方法中定义 Mock 功能(在调用要测试的方法之前)。
  5. 使用该对象,调用您正在测试的方法...模拟应该被挂接并按照前面步骤中定义的方式工作。@InjectMock

因此,对于我上面使用的示例类,测试/模拟的代码将作为模拟返回,您可以对其进行任何操作。根据我问题中上面的示例,这是代码的样子: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();
  }
}

答案 2

这里有几种方法:

  1. ReflectionTestUtils弹簧测试框架: .这样,您就不会引入另一个依赖项。ReflectionTestUtils.setField(objectToTest, "privateFieldName", mockObjectToInject);
  2. org.mockito.internal.util.reflection.FieldSetter.
  3. PowerMock.Whitebox.setInternalState()来模拟一个私人领域。

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

仅使用Mockito,您就无法模拟局部变量的创建,因为不起作用;将返回空值。它可以编译,但不起作用。when(any(Foo.class)

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


推荐