ykluo

导航

嵩山少林之单元测试篇

前言

当你看到这份文档时,你会是什么样的心情呢?

想问一下正在食用这份文档的大佬在平时工作中是否遇见过、听到过:
1、我是个很棒的程序员, 我是不是可以不进行单元测试?
2、在每个开发组织中都至少有一个这样的开发人员,他非常擅长于编程,他们开发的软件总是在第一时间就可以正常运行,为什么要写单元测试不是在浪费时间吗?
 
收起你的小心思和借口,在真实世界里,每个人都会犯错误,总觉得哪里会突然冒出莫名其妙的bug,也怕别人不小心改了自己的代码(被害妄想症),新版本上线提心吊胆......花点时间写单元测试,有事没事跑一下测试,确保原逻辑没问题。
玩笑过后。。。。
那么接下来为了我们每天不至于那么提心吊胆,至少能睡安稳一点,一起来了解下单元测试对我们开发人员的重要性:

什么是单元测试

是针对 程序的最小单元 来进行正确性检验的测试工作。

 

为什么我们需要写单元测试

我们当然会在开发的时候进行项目功能的测试,常用手段诸如用main对指定的代码块验证,或者使用postman对我们设计的接口进行测试验证,但是这些方法是一定程度上可以完成当期内的功能需求的,
否则也不会有那么多的“单元测试真的有用吗”的这种声音。
那么中间的问题是什么呢?
答案是你无法永远保证“当期的业务测试”就是能覆盖你本期提供的功能点,以及即便是测试同学保存
有以往所有测试用例的自动化测试内容,也无法真正的保证你的系统是完好的,因为业务功能和软件
功能中间是有隔阂的。针对用例设计的功能测试,无法保证你的“系统”正常!!
 
所以这就是为什么我们需要写单元测试。

单元测试基础知识

什么是断言

在写单元测试之前,我们需要,以及常见用法:
assertEquals
判断两个对象或两个原始类型是否相等
assertNotEquals
判断两个对象或两个原始类型是否相等
assertSame
判断两个对象是否指向同一个对象
assertNotSame
判断两个对象引用是否指向不同的对象
assertTrue
判断给定的布尔值是否为true
assertFalse
判断给定的布尔值是否为false
assertNull
判断给定的对象引用是否为null
assertNotNull
判断给定的对象引用是否不为null
assertArrayEquals
判断两个对象或者原始类型的数组是否相等
assertThrows
判断异常 配合函数式编程就可以进行使用
assertTimeout
测试方法设置了超时时间
fail
直接使得测试失败
assertAll
接受多个org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过lambda表达式很容易的提供这些断言。
 

单元测试用例相关概念

正面测试(Positive Testing)

测试被测对象的正确功能实现无误,即正常流程功能。往往需要根据设计说明进行用例导出,严格按照设计说明编写即可,用例划分注意等价类区分等方法。

负面测试(Negative Testing)

测试被测对象的异常功能实现无误,多在异常流程,异常数据中体现。该部分测试需要对被测对象进行错误发散,常依赖于边界值区分等方法。

分支测试

使用流程图,明确可能出现的每条分支,制造响应的数据进行覆盖,实现对被测对象的测试。这个过程对于分支可以进行响应的简化,可以穿插等价类等方法去除同类分支。

边界值分析法

这种方法更偏向于黑盒测试用例设计中使用,对被测输入进行边界分析,从各个角度都会有边界值,例如程序内部依赖之间,已经有一些边界存在,在程序集成展示后,也会有新的边界出现,在设计的时候,需要注意这些细节。例如我们可输入范围是3-6,和输入类型为浮点数。那么边界值为7-8之间
 

编写单元测试

单元测试基础环境

准备 : JDK -1.8 SpringBoot -2.6.6 H2 -1.4.195

搭建项目

一般项目Demo已经提供 可以忽略,如果想自己进行单元测试,可以按照以下步骤:
pom.xml
我们在搭建完项目之后;因为使用H2数据库来做单元测试数据库所以需要添加H2依赖:
<!--引入h2数据库的依赖 -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.195</version>
    <scope>test</scope>
</dependency>

 

 目录结构
当你看到目录结构图时:可能会有疑问?test 中的包也需要自己去创建吗?
答案是:不需要!!!idea 可以替我们去做这些事!!
 
到此创建基础单元测试模块都已经完成了,是一个什么样子呢?我们一起来看一下:
 
主要我们看一下 :application.yml
spring:
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:test
  sql:
    init:
      schema-locations: classpath:sql/schema.sql
      data-locations: classpath:sql/init.sql

 单元测试基础示例

到这里相信大家对断言相关知识有了一定的了解。
废话不多说直接上代码,我们一起看一下单元测试如何编写:
示例:
需求:获取单个用户信息。
/**
* 异常枚举类
*
* @author ykluoh
* @since 1.0.0
*/
@Getter
public enum LykExceptionEnum {
    
    /**
    * 判断对象中属性值是否全为空
    */
    CHECK_OBJ_ALL_FIELDS_IS_NULL("110001", "根据ID查询 未查到该用户"),
    
    
    private final String code;
    private final String msg;
    
    LykExceptionEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    
    
    public static LykExceptionEnum getEnum(String value) {
        for (LykExceptionEnum e : LykExceptionEnum.values()) {
            if (e.code.equals(value)) {
                return e;
            }
        }
        return null;
    } 
}
/**
 * JunitUser接口
 *
 * @author ykluoh
 * @since 1.0.0
 */
public interface JunitUserService {


    /**
     * 该方法根据ID来查询单个用户
     *
     * @param id  用户id
     * @return  返回的是:根据id查询之后的用户表的实体
     * @see LykExceptionEnum 该实现类可能会出现 查询为空的异常:CHECK_OBJ_ALL_FIELDS_IS_NULL
     **/
    JunitUser selectById(Long id);
}
/**
 * JunitUserService 接口的实现类
 *
 * @author ykluoh
 * @since 1.0.0
 */
@Slf4j
@Service
public class JunitUserServiceImpl implements JunitUserService {


    @Resource
    private JunitUserDao junitUserDao;


   
    @Override
    public JunitUser selectById(Long id) {
        JunitUser junitUser = junitUserDao.selectById(id);
        if (Objects.isNull(junitUser)) {
            throw new CommonException(LykExceptionEnum.CHECK_OBJ_ALL_FIELDS_IS_NULL.getCode(), LykExceptionEnum.CHECK_OBJ_ALL_FIELDS_IS_NULL.getMsg());
        }
        return junitUser;
    }
}
@Slf4j
class JunitUserServiceImplTest extends LykProjectApplicationTests {




    @Resource
    private JunitUserService junitUserService;


    @Test
    void selectById() {
        CommonException commonException;
        Long selectById = 1L;
        JunitUser junitUser = junitUserService.selectById(selectById);
        Assertions.assertNotNull(junitUser);
        Long selectByIdNull = 2L;
        commonException = Assertions.assertThrows(CommonException.class, () -> {
            junitUserService.selectById(selectByIdNull);
        });
        Assertions.assertEquals(LykExceptionEnum.CHECK_OBJ_ALL_FIELDS_IS_NULL, LykExceptionEnum.getEnum(commonException.getCode()));
    }


}

单元测试规约

规约第8条提到了:语句覆盖率达到70%;核心模块的语句覆盖率和分支覆盖率都要达到 100% .
那么我们写完单元测试之后如何查看覆盖率呢?
 

单元测试特殊情况处理

异常处理

1、Assertions.assertThrows() :已经在示例中演示了。
 

参考

 

 
 

posted on 2022-06-08 16:22  武汉彭于晏  阅读(140)  评论(0编辑  收藏  举报