嘲笑假定存在的私有变量

时间:2013-10-22 14:50:14

标签: java mocking mockito powermock

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

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

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

作为参考,代码在createConnection()上调用QueueConnectionFactory,因为Factory尚未初始化/模拟,因此抛出空指针异常。

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

  ...

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

2 个答案:

答案 0 :(得分:37)

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

当您拥有永远不会初始化的私有成员变量(并假设在其他地方创建)时,您可以使用@InjectMocks注释将您想要的模拟“注入”您正在测试的类中。

  1. 在测试类的测试类中添加一个变量,并为其添加注释@InjectMocks(org.Mockito.InjectMocks)。
  2. 使用@Mock注释设置要注入的模拟。使用@Mock (name = "privateVariableNameHere") name属性将Mock对象映射到您正在测试的类中的私有变量。
  3. 在设置功能中或在调用类之前,初始化模拟。我找到的最简单的方法是使用带有@Before注释的“setup”方法。然后在里面调用MockitoAnnotations.initMocks(this);以使用@Mock注释快速初始化任何内容。
  4. 在测试方法中定义Mock功能(在调用您正在测试的方法之前)。
  5. 使用@InjectMock对象,调用您正在测试的方法......应该按照前面步骤中的定义将模拟连接起来并进行工作。
  6. 因此,对于我上面使用的示例类,test / mock的代码将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();
      }
    }
    

答案 1 :(得分:0)

这里有几种方法:

  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)不起作用。将返回null。它可以编译,但是不起作用。

参考: Mockito: Mock private field initialization