powerMock和mockito使用

  • 2020 年 8 月 16 日
  • 笔记

powerMock和mockito

  • powermock和mockito都是做mock的框架,powermock在mockito的基础上扩展而来,支持mockito的操作(也支持别的mock框架比如easyMock)。因此在maven引入powermock的时候,需要引mockito的包。powermock和mockito版本上要配合着使用。powermock在mockito的基础上,扩展了对static class, final class,constructor,private method等的mock操作。慎用这些mock,因为在一个良好的设计里,static final private这些class和method是不需要被测试的,会被public方法调用,只要测试public就好。

使用

  • maven引入
<properties>
    <powermock.version>2.0.2</powermock.version>
</properties>
<dependencies>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
</dependencies>
  • 和spring配合使用的时候,要特别注意版本的问题,如果测试起不来的话,先确认下powermock的版本和spring是否对应。

注解

  • @powermockIgnore,默认情况下,powermock试图使用自己的classLoader去loader所有的class,除里system class(java.lang等目录下的class),使用powermockIgnore声明的class,pwoermock也不会加载。

白盒测试

  • powermock提供了几种方法来处理私有方法,私有变量,私有构造函数
  • Use Whitebox.setInternalState(..) to set a non-public member of an instance or class.
  • Use Whitebox.getInternalState(..) to get a non-public member of an instance or class.
  • Use Whitebox.invokeMethod(..) to invoke a non-public method of an instance or class.
  • Use Whitebox.invokeConstructor(..) to create an instance of a class with a private constructor.

mock构造函数

whenNew(MyClass.class).withNoArguments().thenThrow(new IOException(“error message”));

@RunWith(PowerMockRunner.class)
@PrepareForTest(X.class)
public class XTest {
        @Test
        public void test() {
                whenNew(MyClass.class).withNoArguments().thenThrow(new IOException("error message"));

                X x = new X();
                x.y(); // y is the method doing "new MyClass()"

                ..
        }
}
  • prepare的时候,prepareForTest的类是调用MyClass的类。

Delegate to another JUnit Runner

  • spring和powermock结合使用时,使用以下联合注解
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@PrepareForTest(Test.class)
  • powerMock还是由自己的runner来做object的mock工作,在执行时,再交给delegate的runner去执行。
  • 不同的runner结合使用//codete.com/blog/testing-spring-boot-application-with-junit-and-different-runners/

各种runner是何时如何起作用的呢

  • 首先runner是干啥的?
    • runner其实就是各个框架在跑测试case的前后处理一些逻辑。
    • 比如在Junit框架中,我们什么都不写,用Junit默认的Runner BlockJUnit4ClassRunner来执行case,主要做什么事情呢?就是处理Junit框架中的一些注解,比如扫到那些所有@Test的注解,这些是要跑的case,将那些@Ignore的注解的case忽略掉,在执行test case的前后,执行那些@Before和@after的注解的方法。
  • BlockJUnit4ClassRunner
 @Override
    protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
        Description description = describeChild(method);
        if (method.getAnnotation(Ignore.class) != null) {
            notifier.fireTestIgnored(description);
        } else {
            runLeaf(methodBlock(method), description, notifier);
        }
    }

    @Override
    protected Description describeChild(FrameworkMethod method) {
        return Description.createTestDescription(getTestClass().getJavaClass(),
                testName(method), method.getAnnotations());
    }

    @Override
    protected List<FrameworkMethod> getChildren() {
        return computeTestMethods();
    }
  • 主要是这三个方法,getChildren得到所有@Test注解的方法。runChild中,将要调用的方法组织好,最后通过反射调用这个方法执行,同时处理执行成功或者执行失败的结果。

  • mockito的runner,JUnit44RunnerImpl,在跑test之前,将@Mock注解的对象构造出来。

  • SpringJUnit4ClassRunner 在test class中做依赖注入。

  • 总之,就是各个不同的runner在处理各自框架的职责。mockitorunner的就是负责mock,spring的runner就是负责依赖注入。

  • 这里也就解释了powermock的delegate是如何work的: testClass首先会交给powermockRunner完成自己的mock的工作,然后再交给springRunner去完成依赖注入的工作。

  • //dzone.com/articles/understanding-junits-runner

  • //codete.com/blog/testing-spring-boot-application-with-junit-and-different-runners/

参考