《Java单元测试实战》——无效单测:那些年,我们写过的无效单元测试(1)

简介: 《Java单元测试实战》——无效单测:那些年,我们写过的无效单元测试(1)
+关注继续查看

无效单测:那些年,我们写过的无效单元测试

前言

 

那些年,为了学分,我们学会了面向过程编程;

那些年,为了就业,我们学会了面向对象编程;

那些年,为了生活,我们学会了面向工资编程;

那些年,为了升职加薪,我们学会了面向领导编程;

那些年,为了完成指标,我们学会了面向指标编程;

……

那些年,我们学会了敷衍地编程;

那些年,我们编程只是为了敷衍。

 

现在,领导要响应集团提高代码质量的号召,需要提升单元测试的代码覆盖率。当然,我们不能让领导失望,那就加班加点地补充单元测试用例,努力提高单元测试的代码覆盖率。至于单元测试用例的有效性,我们大抵是不用关心的,因为我们只是面向指标编程。

 

我曾经阅读过一个Java服务项目,单元测试的代码覆盖率非常高,但是通篇没有一个依赖方法验证(Mockito.verify)、满纸仅存几个数据对象断言(Assert.assertNotNull)。我说,这些都是无效的单元测试用例,根本起不到测试代码BUG和回归验证代码的作用。后来,在一个月黑风高的夜里,一个新增的方法调用,引起了一场血雨腥风。

 

编写单元测试用例的目的,并不是为了追求单元测试代码覆盖率,而是为了利用单元测试验证回归代码——试图找出代码中潜藏着的BUG。所以,我们应该具备工匠精神、怀着一颗敬畏心,编写出有效的单元测试用例。在这篇文章里,作者通过日常的单元测试实践,系统地总结出一套避免编写无效单元测试用例的方法和原则。

 

一、 单元测试简介

 

1. 单元测试概念

 

在维基百科中是这样描述的:

 

在计算机编程中,单元测试又称为模块测试,是针对程序模块来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类、抽象类、或者派生类中的方法。

 

2. 单元测试案例

 

首先,通过一个简单的服务代码案例,让我们认识一下集成测试和单元测试。

 

1) 服务代码案例

 

这里,以用户服务(UserService)的分页查询用户(queryUser)为例说明。


image.png

2) 集成测试用例

 

很多人认为,凡是用到JUnit测试框架的测试用例都是单元测试用例,于是就写出了下面的集成测试用例。

 

image.png


集成测试用例主要有以下特点:

 

• 依赖外部环境和数据;

• 需要启动应用并初始化测试对象;

• 直接使用`@Autowired`注入测试对象;

• 有时候无法验证不确定的返回值,只能靠打印日志来人工核对。

 

3) 单元测试用例

 

采用JUnit+Mockito编写的单元测试用例如下:


@Slf4j
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
    /** 定义静态常量 */
    /** 资源路径 */
    private static final String RESOURCE_PATH = "testUserService/";

    /** 模拟依赖对象 */
    /** 用户DAO */
    @Mock
    private UserDAO userDAO;

    /** 定义测试对象 */
    /** 用户服务 */
    @InjectMocks
    private UserService userService;

    /**
     * 测试: 查询用户-无数据
     */
    @Test
    public void testQueryUserWithoutData() {
        // 模拟依赖方法
        // 模拟依赖方法: userDAO.countByCompany
        Long companyId = 123L;
        Long startIndex = 90L;
        Integer pageSize = 10;
        Mockito.doReturn(0L).when(userDAO).countByCompany(companyId);

        // 调用测试方法
        String path = RESOURCE_PATH + "testQueryUserWithoutData/";
        PageDataVO<UserVO> pageData = userService.queryUser(companyId, startIndex, pageSize);
        String text = ResourceHelper.getResourceAsString(getClass(), path + "pageData.json");
        Assert.assertEquals("分页数据不一致", text, JSON.toJSONString(pageData));

        // 验证依赖方法
        // 验证依赖方法: userDAO.countByCompany
        Mockito.verify(userDAO).countByCompany(companyId);

        // 验证依赖对象
        Mockito.verifyNoMoreInteractions(userDAO);
    }

    /**
     * 测试: 查询用户-有数据
     */
    @Test
    public void testQueryUserWithData() {
        // 模拟依赖方法
        String path = RESOURCE_PATH + "testQueryUserWithData/";
        // 模拟依赖方法: userDAO.countByCompany
        Long companyId = 123L;
        Mockito.doReturn(91L).when(userDAO).countByCompany(companyId);
        // 模拟依赖方法: userDAO.queryByCompany
        Long startIndex = 90L;
        Integer pageSize = 10;
        String text = ResourceHelper.getResourceAsString(getClass(), path + "dataList.json");
        List<UserVO> dataList = JSON.parseArray(text, UserVO.class);
        Mockito.doReturn(dataList).when(userDAO).queryByCompany(companyId, startIndex, pageSize);

        // 调用测试方法
        PageDataVO<UserVO> pageData = userService.queryUser(companyId, startIndex, pageSize);
        text = ResourceHelper.getResourceAsString(getClass(), path + "pageData.json");
        Assert.assertEquals("分页数据不一致", text, JSON.toJSONString(pageData));

        // 验证依赖方法
        // 验证依赖方法: userDAO.countByCompany
        Mockito.verify(userDAO).countByCompany(companyId);
        // 验证依赖方法: userDAO.queryByCompany
        Mockito.verify(userDAO).queryByCompany(companyId, startIndex, pageSize);

        // 验证依赖对象
        Mockito.verifyNoMoreInteractions(userDAO);
    }
}

单元测试用例主要有以下特点:

 

• 不依赖外部环境和数据;

• 不需要启动应用和初始化对象;

• 需要用@Mock来初始化依赖对象,用@InjectMocks来初始化测试对象;

• 需要自己模拟依赖方法,指定什么参数返回什么值或异常;

• 因为测试方法返回值确定,可以直接用Assert相关方法进行断言;

• 可以验证依赖方法的调用次数和参数值,还可以验证依赖对象的方法调用是否验证完毕。

 



《Java单元测试实战》——无效单测:那些年,我们写过的无效单元测试(2) https://developer.aliyun.com/article/1232114?groupCode=java




 

相关文章
|
2月前
|
JSON 前端开发 Java
Java基础知识第九讲:单元测试、前后端规约与联调
Java基础知识第九讲:单元测试、前后端规约与联调
|
4月前
|
Java 测试技术
深入探索 Java 中的 @Test 注解:优化单元测试流程的利器
在软件开发中,单元测试是保障代码质量和稳定性的重要手段之一。Java 中的 `@Test` 注解则为开发人员提供了一种方便、高效的方式来编写和执行单元测试。通过该注解,我们可以轻松地标记测试方法,自动化运行测试,并确保代码在各种情况下的正确性。本文将带您深入探索 Java 中的 `@Test` 注解,揭示其作用、用法以及在实际开发中的应用场景。
|
4月前
|
Java 测试技术
Java中JUnit单元测试
Java中JUnit单元测试
52 0
|
5月前
|
Java 测试技术 程序员
Java单元测试
Junit的简单使用说明
|
5月前
|
Java 测试技术 程序员
【Java】Java(四十七):单元测试
1. 概述 JUnit是一个 Java 编程语言的单元测试工具。JUnit 是一个非常重要的测试工具 2. 特点 JUnit是一个开放源代码的测试工具。 提供注解来识别测试方法。 JUnit测试可以让你编写代码更快,并能提高质量。 JUnit优雅简洁。没那么复杂,花费时间较少。 JUnit在一个条中显示进度。如果运行良好则是绿色;如果运行失败,则变成红色。
|
6月前
|
设计模式 安全 Java
【Java设计模式 规范与重构】 二 重构的保障:单元测试,以及如何提高代码可测试性
【Java设计模式 规范与重构】 二 重构的保障:单元测试,以及如何提高代码可测试性
90 0
|
6月前
|
Java 测试技术
idea单元测试(导入Junit4的Java包到项目中)
idea单元测试(导入Junit4的Java包到项目中)
222 0
|
7月前
|
JSON 安全 Java
《Java单元测试实战》——卷首语
《Java单元测试实战》——卷首语
163 0
|
7月前
|
Java 测试技术 Maven
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(1)
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(1)
204 0
|
7月前
|
Java 测试技术
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(2)
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(2)
127 0
热门文章
最新文章
热门文章
最新文章
推荐文章
更多