每个 @InjectMocks 调用都没有考虑 PowerMock 静态方法模拟

PowerMock static method mocking isn't taken into account for each @InjectMocks invocation

我想编写一些使用 JUnit 4.12、Mockito 1.9.5 和 PowerMock 1.6.1 的单元测试。 class 有一些字段用@Mock 注释,还有一些字段用@InjectMocks 注释。 用@InjectMocks 注释的属性在某个点到达父构造函数,其中包含一些静态方法调用,应该用 PowerMock 模拟。 问题是第一个测试无缝运行,而第二个测试似乎根本没有模拟静态方法。

@RunWith(PowerMockRunner.class)
@PrepareForTest({ StaticClass.class })
public class TestClass {
  @Mock
  private SomeClass attribute1;
  @InjectMocks
  private SomeOtherClass attribute2;

  @BeforeClass
  public static void setUp() {
    PowerMockito.mockStatic(StaticClass.class);

 when(StaticClass.staticMethod(any(), any()).thenReturn(new SomeConcreteClass());
  }

  @Test
  public void test1() {
    assertEquals(attribute2.method1(), value1);
  }

  @Test
  public void test2() {
    assertEquals(attribute2.method2(), value2);
  }
}

public class SomeOtherClass {
  private SomeClass attribute;
  public SomeOtherClass() {
    SomeConcreteClass value = StaticClass.staticMethod(argument1, argument2);
    value.someOtherMethod();
  }
}

如前所述,第一个测试通过并且 StaticClass.staticMethod() 被 PowerMock 预期的模拟。 第二个测试没有通过,当对值调用 someOtherMethod 时,它在行中抛出 NullPointerException(因为 value = null,因为 StaticClass.staticMethod 不再被 PowerMock 模拟)。

如 (Mocking behaviour resets after each test with PowerMock) 中所述,Powermock 在每次测试前重置模拟。

出于某种原因,它第一次起作用 - 存在 该问题的未解决错误报告 (https://github.com/powermock/powermock/issues/398)。


它的设计可以说是糟糕的,但可以按照以下方式做你想做的事情:
不要依赖注释,而是手动设置模拟。

private SomeClass attribute;
private SomeOtherClass testClass;

@Before
public void setUp() {
    PowerMockito.mockStatic(StaticClass.class);
    Mockito.when(StaticClass.staticMethod(anyString())).thenReturn(new SomeConcreteClass());

    attribute = Mockito.mock(SomeClass.class);      
    testClass = new SomeOtherClass();

    // assign mock manually
    testClass.attribute = attribute;
}

首选方法是使用 SomeOtherClass
的构造函数提供 attribute,但是因为您似乎使用了空构造函数 您将不得不从外部设置值。
如果 attribute 实例不可访问,您可能被迫使用反射。


更简洁的方法是重构您 SomeOtherClass 的构造函数,使其不在内部使用静态方法。而是将 SomeConcreteClass 作为参数传递给构造函数是可行的方法。

有些人甚至说你不应该在构造函数中包含任何逻辑。