有了它(powermock)再也不担心单元测试不达标了


为什么要写单元测试

  • 优点:单元测试可以减少bug率,提升代码的质量。还可以通过单元测试来熟悉业务。
  • 公司硬性要求:有些公司可能还会强制要求,每次新增代码、或者变更代码单测覆盖率要达到多少比例才能申请代码合并请求。

    选择哪个单元测试框架

    目前应用比较普遍的java单元测试工具 junit4+Mock(Mockito、jmock、EasyMock、powermock)。为什么会选择powermock?
    在做单元测试的时候,我们会发现我们要测试的方法会有很多外部依赖的对象或者一些其他服务的调用比如说(发送邮件,网络通讯,soa调用)。 而我们没法控制这些外部依赖的对象。 为了解决这个问题,我们需要用到Mock来模拟这些外部依赖的对象,从而控制它们。只关心我们自己的业务逻辑是否正确。而这时powermock就起作用了,它不仅可以mock外部的依赖,还可以mock私有方法、final方法,总之它的功能很强大。

    什么是powerMocker

    PowerMock是一个框架,它以更强大的功能扩展了其他模拟库,例如EasyMock。 PowerMock使用自定义的类加载器和字节码操作来模拟静态方法,构造函数, 最终类和方法,私有方法,删除静态初始化程序等。通过使用自定义类加载器,无需对IDE或持续集成服务器进行任何更改,从而简化了采用过程。熟悉受支持的模拟框架的开发人员会发现PowerMock易于使用,因为整个期望API都是相同的,
    无论是静态方法还是构造函数。PowerMock 旨在通过少量方法和注释扩展现有的API,以启用额外的功能。

常用注解

  • @RunWith(PowerMockRunner.class)
    告诉JUnit使用PowerMockRunner进行测试
  • @PrepareForTest({DemoDao.class})
    所有需要测试的类列在此处,适用于模拟final类或有final, private, static, native方法的类
    • @PowerMockIgnore({“javax.management.“, “javax.net.ssl.“})
      为了解决使用powermock后,提示classloader错误
    • @SuppressStaticInitializationFor
      不让静态代码加载
      其他更多注解可以参考:https://github.com/powermock/powermock/wiki/Suppress-Unwanted-Behavior

      如何开始

      JUnit 4.4及以上
      <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-mockito2</artifactId>
       <version>${powermock.version}</version>
       <scope>test</scope>
      </dependency>
      </dependencies>

      powerMock样例

      这是一个需要被mock的类里面有私有方法、静态方法、等等下面一一来演示各个方法的mock功能。
      ```java
      /**

*

  • @Date: 2020/3/31

  • @Description:

  • /
    @Repository
    public class DemoDao {
    public String mockPublicMethod(String type) throws Throwable {

      throw  new Throwable();

    }
    public final String mockFinalMethod(String type) throws Throwable {

      throw  new Throwable();

    }
    public static String mockStaticMethod(String type) throws Throwable {

      throw  new Throwable();

    }
    }
    /**

  • @Date: 2020/3/31 11:34

  • @Description:

  • /
    @Component
    public class DemoService extends AbstractDemo{

    @Autowired
    private DemoDao demoDao;

    public String mockPublicMethod() throws Throwable {

      return demoDao.mockPublicMethod("demo");

    }

    public String mockFinalMethod() throws Throwable {

      return demoDao.mockFinalMethod("demo");

    }

    public String mockStaticMethod() throws Throwable {

      return DemoDao.mockStaticMethod("demo");

    }

private String callPrivateMethod(String type) {
    return type;
}

public String mockPublicMethodCallPrivateMethod(String type) throws Throwable {
    return callPrivateMethodThrowable(type);
}

private String callPrivateMethodThrowable(String type) throws Throwable {
    throw new Throwable();
}

public String mockExtendMethod(String type) throws Throwable {
   return getExtendMethod();
}

public static String UUID = "uuid";

}

##### mock普通公共方法
   ```java
      /**
       * @Date: 2020/4/24 14:22
       * @Description:
       */
      @RunWith(PowerMockRunner.class)
      public class DemoServiceTest {

          @InjectMocks
          private DemoService demoService;
          @Mock
          private DemoDao demoDao;

       /**
       * mock 普通方法
       * @throws Throwable
       */
          @Test
          public void mockPublicMethod() throws Throwable {
              String type = UUID.randomUUID().toString();
              PowerMockito.when(demoDao.mockPublicMethod(any())).thenReturn(type);
              String result = demoService.mockPublicMethod();
              Assert.assertEquals(type, result);
          }
mock Final方法

跟普通方法是一样的,唯一的区别是需要在类上加入PrepareForTest注解

    @RunWith(PowerMockRunner.class)
    @PrepareForTest(DemoDao.class)
    public class DemoServiceTest {

        @InjectMocks
        private DemoService demoService;
        @Mock
        private DemoDao demoDao;


      /**
          * mock final方法
          * @throws Throwable
          */
         @Test
         public void mockFinalMethod() throws Throwable {
             String type = UUID.randomUUID().toString();
             PowerMockito.when(demoDao.mockFinalMethod(any())).thenReturn(type);
             String result = demoService.mockFinalMethod();
             Assert.assertEquals(type, result);
         }
mock静态方法(使用 PowerMockito.mockStatic)被mock的类也要用PrepareForTest注解修饰。
  @RunWith(PowerMockRunner.class)
  @PrepareForTest(DemoDao.class)
  public class DemoServiceTest {

      @InjectMocks
      private DemoService demoService;
      @Mock
      private DemoDao demoDao;
      /**
       * mock 静态方法
       * @throws Throwable
       */
      @Test
      public void mockStaticMethod() throws Throwable {
          String type = UUID.randomUUID().toString();
          PowerMockito.mockStatic(DemoDao.class);
          PowerMockito.when(DemoDao.mockStaticMethod(any())).thenReturn(type);
          String result = demoService.mockStaticMethod();
          Assert.assertEquals(type, result);
      }
调用 private方法
    /**
     * 调用私有方法
     * 
     * @throws Throwable
     */
     /**
        * 调用私有方法
        * 
        * @throws Throwable
        */
       @Test
       public void callPrivateMethod() throws Throwable {
           // 第一种方式
           String type = UUID.randomUUID().toString();
           Method method = PowerMockito.method(DemoService.class, "callPrivateMethod", String.class);
           String result = (String) method.invoke(demoService, type);
           Assert.assertEquals(type, result);

           //第二种方式
           String result1 = Whitebox.invokeMethod(demoService, "callPrivateMethod", type);
           Assert.assertEquals(type, result1);
       }
mock 私有方法(被mock的类也要用PrepareForTest注解修饰。)
    /**
     * mock私有方法
     *
     * @throws Throwable
     */
    @Test
    public void mockPrivateMethod() throws Throwable {
        String type = UUID.randomUUID().toString();
        // 重点这一句
        demoService = PowerMockito.spy(demoService);
        PowerMockito.doReturn(type).when(demoService,"callPrivateMethodThrowable",type);
        String result = demoService.mockPublicMethodCallPrivateMethod(type);
        Assert.assertEquals(type, result);
    }
mock父类方法
     /**
       * mock父类方法
       *
       * @throws Throwable
       */
      @Test
      public void mockExtendMethod() throws Throwable {
          String type = UUID.randomUUID().toString();
          // 需要mock的父类的方法
          Method method = PowerMockito.method(AbstractDemo.class, "getExtendMethod");
          // InvocationHandler
          PowerMockito.replace(method).with((proxy, method1, args) -> type);
          String result = demoService.mockExtendMethod(type);
          Assert.assertEquals(type, result);
      }
mock构造方法
     public DemoService() {
            throw new NullPointerException();
        }
    @Test
    public void mockConstructorMethod() throws Throwable {
        PowerMockito.whenNew(DemoService.class).withNoArguments().thenReturn(demoService);
    }
mock字段
        /**
         * mock 字段
         */
        @Test
        public void mockFiled(){
            String uuid = UUID.randomUUID().toString();
            Whitebox.setInternalState(DemoService.class, "UUID",uuid);
            Assert.assertEquals(DemoService.UUID, uuid);
        }

结束

  • 由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
  • 如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
  • 感谢您的阅读,十分欢迎并感谢您的关注。

    历史推荐


文章作者: java金融
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 java金融 !
 上一篇
终于有人把 java代理讲清楚了,万字详解! 终于有人把 java代理讲清楚了,万字详解!
什么是代理 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象
2020-09-09
下一篇 
支付业务设计模式优化嵌套if else 支付业务设计模式优化嵌套if else
背景最近在做项目的时候,需要接入支付。由于接入第三方支付而且还不知止一家,需要接入很多家。比如说支付宝、微信、富友支付等。每家支付都一个回调。现如今的代码,根据不同的第三方支付一大堆else if判断。现如今代码如下: public Pa
2020-09-09