powermockito测试私有方法

本文介绍了单元测试在软件开发中的重要性,特别是PowerMock框架在处理外部依赖和模拟私有方法等方面的应用,以及如何在项目中开始使用PowerMock进行有效测试和满足公司代码审查标准。
摘要由CSDN通过智能技术生成

为什么要写单元测试

  • 优点:单元测试可以减少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功能。

/**
 *
 * @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普通公共方法

/**
    * @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
*/
@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);
}


//mock私有map
Field field = CoreBookingDeliveryCache.class.getDeclaredField("coreBookingDeliveryDtoMap");
FieldSetter.setField(coreBookingDeliveryCache, field, builderCoreBookingDeliveryMap());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

M.Rambo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值