美文网首页程序员Java
SpringBoot使用Powermockito单元测试

SpringBoot使用Powermockito单元测试

作者: 西敏寺钟声 | 来源:发表于2020-07-14 22:31 被阅读0次

    mockito框架上手非常简单,但是它也有弊端和局限性,不能mock静态方法、私有方法、构造方法等,但powermockito框架很好的弥补了这一缺陷。

    版本说明

    一般powermockitomockito配合来使用,有相应的版本要求。

    powermockito mockito
    1.6.5+ 2.0.0-beta - 2.0.42-beta
    1.10.19 1.6.4
    1.10.8 - 1.10.x 1.6.2+
    1.9.5-rc1 - 1.9.5 1.5.0 - 1.5.6
    1.9.0-rc1 & 1.9.0 1.4.10 - 1.4.12
    1.8.5 1.3.9 - 1.4.9
    1.8.4 1.3.7 & 1.3.8
    1.8.3 1.3.6
    1.8.1 & 1.8.2 1.3.5
    1.8 1.3
    1.7 1.2.5

    pom配置文件

    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>2.0.0</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito2</artifactId>
        <version>2.0.0</version>
        <scope>test</scope>
    </dependency>
    

    mock静态方法

    什么时候mock静态方法?比如一个类中有许多的public方法,也有static方法,在static方法中调用static方法,但我们并不想mock静态方法中的任何代码,就需要给这个static方法mock一个返回值,mockito框架就无能为力了,因为它并不能mock静态方法,所以需要配合powermockito框架来使用,如下:

    被测试方法

    /**
     * mock静态方法 isTrue
     */
    @GetMapping(value = "/verifyStaticMcok")
    public boolean verifyStaticMcok() {
        String str = "zhangsan";
        boolean flag = isTrue(str);
        log.info("校验姓名:" + flag);
        return flag;
    }
    
    /**
     * 静态方法
     *
     * @param userName 用户名不能为空
     */
    public static boolean isTrue(String userName) {
        return StringUtils.isNotBlank(userName);
    }
    

    测试方法

    测试类上加两个注解,@PrepareForTest可以是class数组。

    @RunWith(PowerMockRunner.class)
    @PrepareForTest(UserSourceController.class)
    public class UserSourceControllerTest {
       // ......
    }
    

    测试启动前,首先mock出静态方法。

    @Before
    public void setUp() {
        mockStatic(UserSourceController.class);
        when(UserSourceController.isTrue(any(String.class))).thenReturn(true);
    }
    

    正常写测试用例就可以了。

    /**
     * mock静态方法
     */
    @Test
    public void verifyStaticMcok_success() {
        boolean flag = userSourceController.verifyStaticMcok();
        assertTrue(flag);
    }
    

    mock私有方法

    查了很多资料,网上答案如出一辙,个人感觉private方法不应该被mock,既然是私有的它也属于本类中代码的原有的一部分,那应该让它走完得出结果,但powermock依然可以做到(通过反射)。什么时候使用?跟静态方法的使用场景一样。

    被测试方法

    /**
     * mock私有方法
     *
     * @param userName 用户名
     */
    @GetMapping(value = "/verifyPrivateMethod")
    public String verifyPrivateMethod(String userName) {
        log.info("传入的用户名:" + userName);
        String result = getUserName(userName);
        return result;
    }
    
    /**
     * 私有方法
     *
     * @param str 传入参数
     */
    private String getUserName(String str) {
        log.info("进入了私有方法" + str);
        return str;
    }
    

    测试方法

    测试类上加两个注解,@PrepareForTest可以是class数组。

    @RunWith(PowerMockRunner.class)
    @PrepareForTest(UserSourceController.class)
    public class UserSourceControllerTest {
       // ......
    }
    

    注释写在了代码里。

    /**
     * mock私有方法
     */
    @Test
    public void verifyPrivateMethod_success() throws Exception {
        // spy被测类,只有被spy出来的类,才可以对私有方法进行mock
        UserSourceController spy = PowerMockito.spy(new UserSourceController());
        // 模拟私有方法(反射),意思是传入"zhangsan",强制返回"laozheng"
        // 方法原型: public static <T> OngoingStubbing<T> when(Object instance, String methodName, Object... arguments) throws Exception;
        PowerMockito.when(spy, "getUserName", "zhangsan").thenReturn("laozheng");
        String userName = spy.verifyPrivateMethod("zhangsan");
        // 验证私有方法被执行了
        PowerMockito.verifyPrivate(spy, Mockito.times(1)).invoke("getUserName", "zhangsan");
        assertEquals("laozheng", userName);
    }
    

    总结

    • 为什么mockito不能mock静态方法?mockito使用继承的方式实现mock的,用CGLIB生成mock对象代替真实的对象进行执行,为了mock实例的方法,你可以在子类中覆盖它,而static方法是不能被子类覆盖的,所以mockito不能mock静态方法。但powermock可以mock静态方法,因为它直接在字节码上工作。
    • @PrepareForTest必须写在类上,不能写到具体的测试方法上否则会报错:java.lang.Exception: No tests found matching Method

    补充

    更新于2020年5月21日

    mock HttpServletRequest

    @Test
    public void integration_test() throws ServiceException {
        MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest();
        mockHttpServletRequest.addHeader("password", "123456");
        WalletHRGrantReq walletHRGrantReq = buildWalletHRGrantReq();
        BaseResponse<WalletHRResp> baseResponse = walletHRController.integration_unencrypted(walletHRGrantReq, mockHttpServletRequest);
        verify(walletHRBusiness, times(1)).dealRequest(any(WalletHRGrantReq.class));
        verify(redisLockService, times(1)).lock(any(String.class), any(Long.class), any(Long.class));
        // 实际值#期望值
        assertThat(baseResponse.getCode(), is("200"));
    }
    

    更新于2020年7月14日

    mock RestTemplate

    JSONObject jsonObject = restTemplate.postForObject(processInstanceUrl, httpEntity, JSONObject.class);
    

    类似这种的,可以这样写:

    when(restTemplate.postForObject(any(String.class), any(HttpEntity.class), eq(JSONObject.class))).thenReturn(mockJSONObject());
    

    相关文章

      网友评论

        本文标题:SpringBoot使用Powermockito单元测试

        本文链接:https://www.haomeiwen.com/subject/tumqhktx.html