前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PowerMock(一):PowerMock的使用

PowerMock(一):PowerMock的使用

作者头像
码农飞哥
发布2021-08-18 11:20:00
7.6K0
发布2021-08-18 11:20:00
举报
文章被收录于专栏:好好学习

您好,我是码农飞哥,感谢您阅读本文!本文主要介绍PowerMock的基本使用。

  • 环境
  • 引入依赖
  • 注解说明
  • mock普通方法
  • mock抛出异常
  • mock新建对象
  • mock无返回值的方法
  • mock被final修饰的方法
  • 参数模糊匹配
  • mock静态方法
  • mock私有方法
  • 总结
  • 参考

为啥要使用PowerMock

现在流行的测试驱动开发TDD(Test-Driven Development) ,是敏捷开发中一项核心实践和技术。也是一种设计方法论。其中最重要的一环就是使用单元测试。单元测试是保证代码质量的一个重要手段,通过单元测试我们可以快速的测试代码的各个分支,各种场景,代码重构时只需要重新跑下单元测试就是能知道代码潜在的问题。单元测试是通过Mock的方式调用被测试的方法,其有如下几个优点:

  1. Mock可以解除测试对象对外部服务的依赖(比如数据库,第三方接口等),使得测试用例可以独立运行。不管是单体应用还是微服务,这点都特别重要。
  2. Mock的第二个好处就是替换外部服务调用,提升测试用例的运行速度。因为任何外部服务调用至少是跨进程级别的消耗,甚至是跨系统、跨网络的消耗,而Mock可以把消耗降低到进程内。
  3. Mock的第三个好处就是提升测试效率,提高单位时间内测试的接口数量。Mock的框架有很多中比如EasyMock等,这里选用PowerMock是因为PowerMock可以用来Mock 私有方法,静态方法以及final方法。EasyMock等则不能。

PowerMock的使用

环境

软件

版本

junit

4.13

powermock

2.0.7

引入依赖

代码语言:javascript
复制
  <properties>
        <powermock.version>2.0.7</powermock.version>
    </properties>
  <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
        <!--powermock开始-->
        <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>
        <!--powermock结束-->

这里引入了是三个依赖,junit依赖如果项目中已有的话,则不需要重复引入,需要注意的是JUnit 4.4及以上版本的JUnit需要引入2.0.x 版本以上的 powermock 。如果项目中有mockito依赖还需要注意mockito的版本与powermock版本对应关系,对应如下图:详细请参考Using PowerMock with Mockito,如果引入的版本不匹配则可能会报如下错误:

代码语言:javascript
复制
java.lang.TypeNotPresentException: Type org.powermock.modules.junit4.PowerMockRunner not present

依赖引入之后就可以编写单元测试代码了。

注解说明

现有一个待测试的类UserServiceImpl,该类中注入了一个UserMapper的类实例。

代码语言:javascript
复制
@Service
public class UserServiceImpl {
  @Autowried
    private UserMapper userMapper;
    ........省略部分方法
}

那么如何对上面的类通过powermock的方式进行单元测试呢?首先是定义一个测试类,定义如下:

代码语言:javascript
复制
@RunWith(PowerMockRunner.class)
@PrepareForTest({UserServiceImpl.class, DateUtil.class, UserMapper.class})
public class UserServiceImplTest {
   @Mock
    private UserMapper userMapper;
    @InjectMocks
    private UserServiceImpl userServiceImpl = new UserServiceImpl();
}

@RunWith(PowerMockRunner.class) 注解表明使用PowerMockRunner运行测试用例,这个必须添加,不然无法使用PowerMock。@PrepareForTest({UserServiceImpl.class, DateUtil.class, UserMapper.class}) @PrepareForTest 注解是用来添加所有需要测试的类,这里列举了三个需要测试的类。@Mock注解修饰会mock出来一个对象,这里mock出来的是UserMapper类实例。@InjectMocks 注解会主动将已存在的mock对象注入到bean中,按名称注入,这个注解修饰在我们需要测试的类上。必须要手动new一个实例,不然单元测试会有问题。这几个注解是一个测试类必须要的。说完了测试类的定义,接下来就让我们来看看各种方法是如何mock的。

mock普通方法

  1. 待测试的方法(UserMapper中)
代码语言:javascript
复制
 boolean saveUser(User user) {
        int i = userMapper.addUser(user);
        return i == 1 ? true : false;
    }

这里的方法int i = userMapper.addUser(user); 有入参,有出参,没有关键字修饰,是一个普通的方法,mock的方式也很简单,就是PowerMockito.when(userMapper.addUser(user)).thenReturn(1); 在when方法中调用你需要mock的方法,thenReturn方法写入你期待返回的值。从字面意思理解就是当调用xxx方法时,返回xxx值。详细的示例如下:2. 测试方法

代码语言:javascript
复制
 User user = new User();
        user.setId(1);
        user.setUserName("test");
        user.setPassword("admin123");
        PowerMockito.when(userMapper.addUser(user)).thenReturn(1);
        boolean result = userServiceImpl.saveUser(user);
        Assert.assertEquals(true, result);

mock抛出异常

单元测试中我们有时候需要mock异常的抛出,其mock的方式也很简单就是在thenThrow(new Exception())写入你期待抛出的异常。如果被mock的方法抛出的是受检异常(checked exception)的话,那么thenThrow抛出new Exception()或者其子类。如果被mock的方法抛出的是非受检异常(unchecked exception),那么thenThrow抛出new RuntimeException或其子类。使用的示范如下:

  1. 待测试的方法(UserMapper中)
代码语言:javascript
复制
 int delUser(int id) throws Exception {
        if (id == -1) {
            throw new Exception("传入的id值不对");
        } else {
            return 1;
        }
    }
  1. 测试方法
代码语言:javascript
复制
   PowerMockito.when(userMapper.delUser(-1)).thenThrow(new Exception());

这里delUser方法抛出的是受检异常Exception,所以在thenThrow中需要new一个Exception对象。

mock新建对象

如果我们要对一个实体对象Bean进行Mock,只需要这样写PowerMockito.whenNew(User.class).withAnyArguments().thenReturn(user)这个代码的意思是创建一个User实例对象,不管传入啥参数都返回定义的实例user,用于替换被测试方法中相应的User对象。使用示范如下:

  1. 待测试的方法(UserMapper中)
代码语言:javascript
复制
 public int countUser() {
        User user = new User();
        int count = 0;
        if (user.getId() > 0) {
            count += 1;
        }
        return count;
    }
  1. 测试方法
代码语言:javascript
复制
 // 6.mock新建对象
        User user = new User();
        user.setId(11);
        PowerMockito.whenNew(User.class).withAnyArguments().thenReturn(user);
        int result = userServiceImpl.countUser();
        Assert.assertEquals(1, result);

这里mock了一个User对象。id是11,当调用countUser方法时可以拿到之前mock的User对象,所以返回的结果是1。

mock无返回值的方法

对于返回值是通过void修饰的方法,他的mock方式与普通方法的mock方式不同。有两种方式mock。方式一:

代码语言:javascript
复制
  PowerMockito.doNothing().when(userMapper, "updateUser", new User());

在when方法中传入userMapper类实例,需要调用的方法名,以及需要传入的参数。方式二:

代码语言:javascript
复制
  PowerMockito.doNothing().when(userMapper).updateUser(user);

在when方法中只传入userMapper类实例,然后通过函数式调用的方式调用待测试的方法。使用示范如下:

  1. 待测试的方法(UserServiceImpl中)
代码语言:javascript
复制
 public void updateUser(User user) {
        userMapper.updateUser(user);
    }
  1. 测试方法
代码语言:javascript
复制
  User user = new User();
        // 4.mock返回值为void的方法
        //方法一
        PowerMockito.doNothing().when(userMapper, "updateUser", new User());
        //方法二
        PowerMockito.doNothing().when(userMapper).updateUser(user);
        userServiceImpl.updateUser(user);

mock被final修饰的方法

现在有一个方法被final关键字修饰,那么该如何要mock这个方法,首先需要mock出一个类实例。如下所示:

代码语言:javascript
复制
    UserMapper mock = PowerMockito.mock(UserMapper.class);

这里需要特别注意的是被mock的类必须要在@PrepareForTest注解中指定,如本例中的@PrepareForTest({UserMapper.class})。不然就会报如下错误:

代码语言:javascript
复制
org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.

使用示范如下:

  1. 待测试的方法(UserMapper中)
代码语言:javascript
复制
    final String getUserName() {
        return "admin";
    }
  1. 测试方法
代码语言:javascript
复制
  UserMapper mock = PowerMockito.mock(UserMapper.class);
  when(mock.getUserName()).thenReturn("123");

参数模糊匹配

前面的测试方法中,参数我们都是指定的,在一些场景下,对于一些比较复杂的参数,我们不好构造,这时候参数模糊匹配就派上用场了。如下所示,现有方法selectUser,他有三个参数,参数类型个不相同。

代码语言:javascript
复制
User selectUser(Integer id, String userName, String password)

当对这个方法进行mock时,可以不用传入具体的参数值。就行这样进行mock。

代码语言:javascript
复制
PowerMockito.when(userMapper.selectUser(ArgumentMatchers.anyInt(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())).thenReturn(user);

其中ArgumentMatchers.anyInt()是指任意的int类型的值,ArgumentMatchers.anyString()是指任意String类型的值。需要特别注意的是一个方法中只要有一个参数使用了模糊匹配,其余的参数也都需要使用模糊匹配。

mock静态方法

对静态方法的mock也比较简单,与普通方法的mock相比只是多了一行代码。就是首先需要对静态方法的所在的类进行mock。

代码语言:javascript
复制
 PowerMockito.mockStatic(DateUtil.class);

同时被mock的类必须要在@PrepareForTest注解中指定,像本例中的DateUtil类。@PrepareForTest({ DateUtil.class}),其他的与普通方法的mock一样,再此就不在赘述了。

mock私有方法

当我们需要测试的方法中调用了一个比较复杂的私有方法时,我们该如何mock呢?针对这种情况PowerMock也可以轻松应对。首先调用spy方法创建出一个新的UserServiceImpl类实例。然后通过这个实例来mock这个私有方法。如下所示:

代码语言:javascript
复制
     UserServiceImpl spy = PowerMockito.spy(userServiceImpl);
  PowerMockito.when(spy, "verifyId", 0).thenReturn(true);

使用示范:

  1. 待测试的方法
代码语言:javascript
复制
  boolean delUser(int id) throws Exception {
        int i = userMapper.delUser(id);
        return verifyId(i);
    }
  1. 测试方法
代码语言:javascript
复制
   public void testVerifyId() throws Exception {
        // 5.mock私有方法
        UserServiceImpl spy = PowerMockito.spy(userServiceImpl);
        PowerMockito.when(spy, "verifyId", 0).thenReturn(true);
        PowerMockito.when(userMapper.delUser(0)).thenReturn(1);
        Assert.assertEquals(userServiceImpl.delUser(0), true);
    }

总结

本文详细介绍了PowerMock的常见使用,PowerMock是一个应用比较广泛的单元测试框架,运用在单元测试中可以很好的提供测试效率。PowerMock可以mock 普通方法,私有方法,静态方法,final修饰的方法。

参考

无所不能的PowerMock,mock私有方法,静态方法,测试私有方法,final类power mock 入门介绍及使用示例

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农飞哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为啥要使用PowerMock
  • PowerMock的使用
    • 环境
      • 引入依赖
        • 注解说明
          • mock普通方法
            • mock抛出异常
              • mock新建对象
                • mock无返回值的方法
                  • mock被final修饰的方法
                    • 参数模糊匹配
                      • mock静态方法
                        • mock私有方法
                          • 总结
                            • 参考
                            相关产品与服务
                            项目管理
                            CODING 项目管理(CODING Project Management,CODING-PM)工具包含迭代管理、需求管理、任务管理、缺陷管理、文件/wiki 等功能,适用于研发团队进行项目管理或敏捷开发实践。结合敏捷研发理念,帮助您对产品进行迭代规划,让每个迭代中的需求、任务、缺陷无障碍沟通流转, 让项目开发过程风险可控,达到可持续性快速迭代。
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档