聊一聊单元测试

1. 单元测试释疑

今天聊聊单元测试。

单元测试的概念,在维基百科中是这样描述的:

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

单元测试侧重的是最小的运行单元,对于大部分的开发同学来说,写完一个Service后,如果项目本身有单元测试覆盖率的要求,多多少少都会写过类似下面的单元测试代码(如果没有单测的要求,我也相信大部分同学是没有写单元测试习惯的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@SpringBootTest(classes = SpringbootOrderApplication.class)
@RunWith(SpringRunner.class)
class OrderServiceTest {

@Autowired
private OrderService orderService;

@BeforeEach
void setUp() {
}

@Test
public void getOrder() {
long orderId = 1L;
Order order = orderService.getOrder(orderId);
assertNotNull(order);
}
}

但是,依据单元测试的定义,这段代码,并不是只测试getOrder这个接口,orderService里也有可能会依赖DAO层的数据,也有可能会依赖下游的RPC接口,因此,这段测试,涉及到完整的查询订单的上下游逻辑,我们可以称之为”集成测试“。

那么问题来了,你说这是集成测试,那我这一段要怎么写单元测试呢?在下一章我们细细道来。

2. 集成测试和单元测试

我们首先总结下集成测试的特点:

  • 需要服务启动并初始化;
  • 需要依赖上下游的数据;
  • 可以使用Springboot的Autowired依赖注入;
  • 上下游链路比较长,有时很难直接用Assert断言判断;

单元测试有如下的特点:

  • 不需要启动应用和初始化;
  • 不依赖上下游的数据;
  • 使用@Mock注解修饰依赖的对象;
  • @InjectMocks来初始化测试对象;
  • 可以直接用Assert相关方法进行断言;

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@RunWith(MockitoJUnitRunner.class)
class OrderServiceTest {

/**
* 定义依赖对象
*/
@MockBean
private IOrderMapper orderMapper;

/**
* 定义测试对象
*/
@InjectMocks
private OrderService orderService;

@Test
void testGetOrder() {
// 注入依赖的方法
long orderId = 3L;
Order orderMock = new Order(orderId, "order-name", 2D, 1);
Mockito.doReturn(orderMock).when(orderMapper).selectOrderById(orderId);

// 验证依赖的方法
Mockito.verify(orderMapper).selectOrderById(orderId);

// 验证service的查询结果
Order orderTest = orderService.getOrder(orderId);
assertNotNull(orderTest);

// 验证依赖的对象
Mockito.verifyNoInteractions(orderMapper);
}
}

3. 什么是好的单侧

在业界,单元测试的标准有AIR原则和FIRST原则。单元测试在线上运行时,感觉像空气(AIR)一样感觉不到,但在测试质量的保障上,却是非常关键的。好的单元测试宏观上来说,具有自动化、独立性、可重复执行的特点。

AIR原则的内容如下:

  • A-Automatic:单元测试应该是全自动执行的,单元
    测试中不准使用System.out来进行人肉验证,必须使用assert来验证;
  • I-Independent:保持单元测试的独立性,单元测试用例之间决不能互相调用,也不能依赖执行的先后次序;
  • R-Repeatable:单元测试是可重复执行的,不应受外部的影响;

FIRST原则的内容如下:

  • F-Fast:单元测试应该是可以快速运行的,在各种测试方法中,单元测试的运行速度是最快的;
  • I-Independent:可以独立运行的,单元测试用例互相之间无依赖,且对外部资源也无任何依赖;
  • R-Repeatable:单元测试应该可以稳定重复的运行,并且每次运行的结果都是稳定可靠的;
  • S-SelfValidating:单元测试应该是用例自动进行验证的,不能依赖人工验证;
  • T-Timely:单元测试必须及时进行编写,更新和维护,以保证用例可以随着业务代码的变化动态的保障质量;

4. 参考文档

以上内容就是关于单元测试的全部内容了,谢谢你阅读到了这里!

Author:zhaoyh