java - 使用PowerMock编写单元测试,mocked方法调用失败

标签 java unit-testing junit mockito powermock

我最近学习使用 PowerMock 为一个名为 Module 的类编写单元测试,该类扩展了 Base 类。它们看起来像这样。

public class Base {
    protected final static ServiceA serviceA;
    protected final static ServiceB serviceB;
    static {
        serviceA = ServiceA.getInstance();
        serviceB = ServiceB.getInstance();
    }
}

public class Module extends Base {
    public DataA methodA() {
        return serviceA.getDataA();
    }
    public DataB methodB() {
        return serviceB.getDataB();
    }
}

我的单元测试如下所示:

@PowerMockIgnore("javax.management.*")
@RunWith(PowerMockRunner.class)
@PrepareForTest({Module.class, ServiceA.class, ServiceB.class})
public class ModuleTest {
    private Module module;
    @Mock
    private ServiceA serviceA;
    @Mock
    private ServiceB serviceB;

    @Before
    public void setup() throws Exception {
        MockitoAnnotations.initMocks(this);

        PowerMockito.mockStatic(ServiceA.class);
        PowerMockito.when(ServiceA.getInstance).thenReturn(serviceA);

        PowerMockito.mockStatic(ServiceB.class);
        PowerMockito.when(ServiceB.getInstance).thenReturn(serviceB);

        module = new Module();
        // I spy it because it has other methods I need to mock
        module = PowerMockito.spy(module);
    }

    @Test
    public void methodATest() {
        DataA dataA = new DataA();
        PowerMockito.when(serviceA.getDataA()).thenReturn(dataA);
        DataA data = module.methodA();
        assertEquals(dataA, data);
    }
    @Test
    public void methodBTest() {
        DataB dataB = new DataB();
        PowerMockito.when(serviceB.getDataB()).thenReturn(dataB);
        DataB data = module.methodB();
        assertEquals(dataB, data);
    }
}

一切看起来都很简单,但是当我运行 ModuleTest 时,methodBTest() 没有通过。似乎 PowerMockito.when(serviceB.getDataB()).thenReturn(dataB) 不起作用并调用真正的 serviceB.getDataB() 方法。因此 assertEquals(dataB, data) 抛出 org.junit.ComparisonFailure

如果我将 methodBTest() 放在 methodATest() 之前,则 methodATest() 不会通过。同样的原因。

如果我放置 PowerMockito.when(serviceA.getDataA()).thenReturn(dataA)PowerMockito.when(serviceB.getDataB()).thenReturn(dataB) 在 setup() 中,一切都运行良好。

这整天都与我接壤。有谁知道为什么会发生这种情况以及如何解决它?我需要在各自的测试方法中编写模拟语句,因为我可能会更改返回值。

最佳答案

这是一个(几乎)不进行任何更改的解决方案

@PowerMockIgnore("javax.management.*")
@RunWith(PowerMockRunner.class)
@PrepareForTest({Module.class, ServiceA.class, ServiceB.class})
public class ModuleTest {
    private Module module;

    private static ServiceA serviceA = Mockito.mock(ServiceA.class);

    private static ServiceB serviceB = Mockito.mock(ServiceB.class);

    @BeforeClass
    public static void oneTimeSetup() throws Exception {
        PowerMockito.mockStatic(ServiceA.class);
        PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA);

        PowerMockito.mockStatic(ServiceB.class);
        PowerMockito.when(ServiceB.class, "getInstance").thenReturn(serviceB);
    }

    @Before
    public void setup() throws Exception {
        module = new Module();
        // I spy it because it has other methods I need to mock
        module = PowerMockito.spy(module);
    }

    @Test
    public void methodATest() {
        DataA dataA = new DataA();
        Mockito.when(serviceA.getDataA()).thenReturn(dataA);
        DataA data = module.methodA();
        assertEquals(dataA, data);
    }
    @Test
    public void methodBTest() {
        DataB dataB = new DataB();
        Mockito.when(serviceB.getDataB()).thenReturn(dataB);
        DataB data = module.methodB();
        assertEquals(dataB, data);
    }
}

更改内容(以及原因):

  • Base :serviceAserviceB更改为 protected ( Module 如果私有(private)则无法访问)
  • PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA); 使用了“正确的”(据我所知)语法
  • 使用了@BeforeClass并制作serviceAserviceB static 来“绕过” Base 中的静态初始化

使用 Junit 4.12、PowerMockito 1.6.2 进行测试。

<小时/>

注意:也可以利用@SuppressStaticInitializationFor为了达到同样的目标:

@SuppressStaticInitializationFor(value = "so46196071.Base") // suppress the static in Base (note this is my package name)
@PowerMockIgnore("javax.management.*")
@RunWith(PowerMockRunner.class)
@PrepareForTest({Module.class, ServiceA.class, ServiceB.class})
public class ModuleBisTest {
    private Module module;

    @Mock
    private ServiceA serviceA;

    @Mock
    private ServiceB serviceB;

    @Before
    public void setup() throws Exception {
        // MockitoAnnotations.initMocks(this); /* this is not needed => done by the runner */

        PowerMockito.mockStatic(ServiceA.class);
        PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA);

        PowerMockito.mockStatic(ServiceB.class);
        PowerMockito.when(ServiceB.class, "getInstance").thenReturn(serviceB);

        module = new Module();
        Whitebox.setInternalState(Base.class, "serviceA", serviceA); // set serviceA in Base "by hand"
        Whitebox.setInternalState(Base.class, "serviceB", serviceB); // set serviceB in Base "by hand"
        // I spy it because it has other methods I need to mock
        module = PowerMockito.spy(module);
    }

    // ...

关于java - 使用PowerMock编写单元测试,mocked方法调用失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46196071/

相关文章:

angularjs - 如何在指令的单元测试中模拟 Angular 翻译过滤器

android - 即使在添加 RxImmediateSchedulerRule 之后,android.os.Looper 中的方法 getMainLooper 仍然没有被模拟

java - 使用 JUnit 测试图形生成

java - 类型不匹配将 Scala JUnit 测试中的数组传递给 Java 方法

java - AEM/Sling - 如何在独立代码中创建 ResourceResolverFactory?

java - 不断收到错误插入变量声明以在我的Java程序上完成VariableDeclaration

java - 我的 ImageView 有一些错误有人知道吗?

java - 菲利克斯 : Configuring bundle cache location to a temporary folder

java - 在 Mockito 中设置后重置模拟的正确替代方法是什么?

java - 在 JMockit 中模拟被测类的私有(private)方法