资讯专栏INFORMATION COLUMN

3.springboot单元测试

anRui / 770人阅读

摘要:单元测试因为公司单元测试覆盖率需要达到,所以进行单元测试用例编写。测试的时候可以把每个判断分支都走到。同这句代码,可以通过如此一个对象,使用以上方法基本上可以编写所有代码的测试类。编写测试一定程度上可以发现代码错误,可以借此重构代码。

3.springboot单元测试
因为公司单元测试覆盖率需要达到80%,所以进行单元测试用例编写。多模块项目的因为会经常调用其他服务,而且避免数据库操作对于数据库造成影响,所以所有的操作都要mock掉,也就是模拟调用的结果。测试用例编写可以参考https://www.journaldev.com/21...,上面介绍也比较详细,不过以下介绍是自己遇到的一些问题。
3.1配置文件

  
        
        6.14.3
        4.12
        2.19.0
        2.0.0-beta.5
    


        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
        
            org.testng
            testng
            ${testng.version}
            test
        
        
        
            junit
            junit
            ${junit4.version}
            test
        
        
        
            org.mockito
            mockito-core
            ${mockito-core.version}
            test
        
        
        
            org.powermock
            powermock-module-testng
            ${powermock.version}
            test
        
        
        
            org.powermock
            powermock-module-junit4
            ${powermock.version}
            test
        
        
        
            org.powermock
            powermock-api-mockito2
            ${powermock.version}
            test
        

可以参考如https://www.journaldev.com/21...

这三个依赖是支持静态类mock所需要的,还有一个版本的问题,之前2.0.0-beta.5的版本是1.7.5遇到了类似

java.lang.NoSuchMethodError: org.mockito.internal.handler.MockHandlerFactory.createMockHandler(Lorg/mockito/mock/MockCreationSettings;)Lorg/mockito/internal/InternalMockHandler;

的报错,修改了版本后来得到了解决。

3.2.没有静态方法调用


要测的serverImpl类使用@InjectMocks,其他所有的依赖的类使用@Mock掉,这样可以模拟依赖的类的实现而不是调用依赖类的实现,放置比如调用数据库操作导致的问题。类上使用@RunWith(SpringRunner.class)注解。

一下为测试代码,withLearningService.getGift(passId, code, token, ip)里面会用到paramUtil.getParamByParamName(PRODUCT_CODE)以及openApiService.userQueryInfoById(anyString()),所以我们需要把用到的两个方法模拟它的返回结果。

when(paramUtil.getParamByParamName(PRODUCT_CODE)).thenReturn(productCode);
when(openApiService.userQueryInfoById(anyString())).thenReturn(intevee);
withLearningService.getGift(passId, code, token, ip);

使用when模拟paramUtil的返回结果,也可以使用any()模拟对象,anyString()来字符串,还有anyInt(),anyLong()模拟其他类型参数。测试的时候可以把每个判断分支都走到。
如果类加上@SpringBootTest注解的话项目会启动springboot项目加载各种配置,速度会慢很多,建议直接都mock掉不需要启动springboot环境。

3.3需要使用静态方法
类上使用
@RunWith(PowerMockRunner.class)
@PrepareForTest({ HttpsUtils.class, RedisUtil.class, SpringContextUtil.class,AESUtil.class,KeyGenerator.class })
两个注解,需要使用静态方法的类添加到@PrepareForTest的参数里面。一般的静态方法调用直接使用:

    PowerMockito.mockStatic(RedisUtil.class);
        PowerMockito.when(RedisUtil.get(anyString())).thenReturn("0");

但是如上面的这个调用会产生类似:

的错误,RedisUtil是我司自己封装的Redis操作工具类,查看RedisUtil.get方法,看到需要注入spring的上下文环境ApplicationContext applicationContext。所以需要添加该上下文环境

   PowerMockito.mockStatic(SpringContextUtil.class);
        PowerMockito.when(SpringContextUtil.getApplicationContext()).thenReturn(new XmlWebApplicationContext());
        PowerMockito.mockStatic(RedisUtil.class);
        PowerMockito.when(RedisUtil.get(anyString())).thenReturn("0");

这样可以解决问题,我自己编写的过程中遇到了一个加解密类报错,工具类代码如下

    if (key == null || content == null || content.isEmpty()) {
            return null;
        }
        String msg = null;

        byte[] byteRresult = new byte[content.length() / 2];

        for (int i = 0; i < content.length() / 2; i++) {
            int high = Integer.parseInt(content.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(content.substring(i * 2 + 1, i * 2 + 2), 16);
            byteRresult[i] = (byte) (high * 16 + low);
        }

        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128);
            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            byte[] result = cipher.doFinal(byteRresult);
            return new String(result);

测试类的mock编写如入下:

        PowerMockito.mockStatic(AESUtil.class);
        PowerMockito.when(AESUtil.decrypt(anyString(), anyString())).thenReturn("11");

这样编写报错,改为以下则完成测试用例编写。

        PowerMockito.mockStatic(KeyGenerator.class);
        KeyGenerator keyg = Mockito.mock(KeyGenerator.class);
        PowerMockito.when(KeyGenerator.getInstance(anyString())).thenReturn(keyg);
        PowerMockito.mockStatic(AESUtil.class);
        PowerMockito.when(AESUtil.decrypt(anyString(), anyString())).thenReturn("11");

同KeyGenerator keyg = Mockito.mock(KeyGenerator.class);这句代码,可以通过如此mock一个对象,使用以上方法基本上可以编写所有代码的测试类。这里的测试最好都是针对server层进行测试,为什么不对controller层也进行测试,其实主要是单元测试的意义是把基本的逻辑跑通,controller层的测试一定程度上可以交由测试进行测试,单元测试如果要覆盖到每一个判断分支也是不好写的,比较痛苦。编写测试一定程度上可以发现代码错误,可以借此重构代码。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/74307.html

相关文章

  • SpringBoot就是这么简单

    摘要:热加载代表的是我们不需要重启服务器,就能够类检测得到,重新生成类的字节码文件无论是热部署或者是热加载都是基于类加载器来完成的。验证阶段字节码文件不会对造成危害准备阶段是会赋初始值,并不是程序中的值。 一、SpringBoot入门 今天在慕课网中看见了Spring Boot这么一个教程,这个Spring Boot作为JavaWeb的学习者肯定至少会听过,但我是不知道他是什么玩意。 只是大...

    whinc 评论0 收藏0
  • SpringBoot整合MyBatis并使用Redis作为缓存组件的Demo

    摘要:本博客猫叔的博客,转载请申明出处本系列教程为项目附带。历史文章如何在安装最新版安装安装最新版的入门教程的入门教程安装教程安装流程安装如果不清楚是什么,请查看的文档和简介,这里给出的安装过程安装虚拟机如果有远程服务器的,请略过此步骤本文推 本博客 猫叔的博客,转载请申明出处本系列教程为HMStrange项目附带。 Auth:HMStrange-TIAN e-mail:zhangqihao...

    mo0n1andin 评论0 收藏0
  • 这样讲 SpringBoot 自动配置原理,你应该能明白了吧

    摘要:这里有一个参数,主要是用来指定该配置项在配置文件中的前缀。创建一个配置类,里面没有显式声明任何的,然后将刚才创建的导入。创建实现类,返回的全类名。创建实现类,实现方法直接手动注册一个名叫的到容器中。前言 小伙伴们是否想起曾经被 SSM 整合支配的恐惧?相信很多小伙伴都是有过这样的经历的,一大堆配置问题,各种排除扫描,导入一个新的依赖又得添加新的配置。自从有了 SpringBoot 之后,咋...

    cc17 评论0 收藏0

发表评论

0条评论

anRui

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<