【问题标题】:Mocking a Private Variable that is Assumed to Exist模拟假定存在的私有变量
【发布时间】:2013-10-22 14:50:14
【问题描述】:

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

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

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

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

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

  ...

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

【问题讨论】:

    标签: java mocking mockito powermock


    【解决方案1】:

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

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

    1. 在你的测试类中为你正在测试的类添加一个变量,并给它注解@InjectMocks (org.Mockito.InjectMocks)。
    2. 使用@Mock 注释来设置要注入的模拟。使用 @Mock (name = "privateVariableNameHere") name 属性将 Mock 对象映射到您正在测试的类中的私有变量。
    3. 在设置函数中或在调用类之前,初始化模拟。我发现最简单的方法是使用带有 @Before 注释的“设置”方法。然后在里面调用MockitoAnnotations.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 个问题
    【解决方案2】:

    这里有几种方法:

    1. ReflectionTestUtils Spring 测试框架: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: Mock private field initialization

    【讨论】:

      猜你喜欢
      • 2018-08-24
      • 1970-01-01
      • 2011-03-18
      • 1970-01-01
      • 1970-01-01
      • 2020-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多