单元测试与测试驱动开发
在学着做lept_json这个项目时,作者在教程中介绍了单元测试的概念,并使用测试驱动开发(Test Driven Development)的方法来循序渐进地完善程序的功能。这篇博客就简单地展开讲一下单元测试和测试驱动开发,并介绍一下在Java中如何使用JUnit库来对代码进行单元测试。
单元测试
单元测试其实挺好理解的,平时在写代码时,有经验的程序员经常会将不同的功能拆分成函数,这样便于对每个函数的正确性进行验证。单元测试大概就是利用这样的思想,对每个模块的功能进行分开检查,以便更快地找到错误所再。其实我们平时写一些简单的程序也会用到单元测试,只不过我们往往会在整个程序运行出错后再进行开发,并且往往是通过调用cout
、printf
等输出函数来检查的。
“正规”的单元测试,是使用断言或第三方的库来对程序的各个功能进行验证的,稍后会介绍用于单元测试Java的JUnit库。
测试驱动开发
前面提到过,单元测试是对程序的子模块进行正确性验证,测试驱动开发就是将验证这一步提前到开发前,也就是先编写测试代码确定需要的功能,再进行开发,使得代码能通过测试。
测试驱动开发的好处有两点,一是可以在开发前就有明确的目标,在开发过程中可以专注功能的实现;二是在功能代码编写完成后,可能需要对代码进行优化、重构,在此过程中有可能会造成功能代码出错,使用测试驱动开发就可以在保持单元测试通过的前提下,随意地进行代码的重构。
当然,测试驱动开发也有缺点,测试开发可能会拖慢开发的进度。另外,一些场景也不适合使用测试驱动开发,例如数据驱动的算法(深度学习)、有物理边界的开发(硬件开发)、无法快速测试的开发等。
JUnit库
这里仅介绍JUnit5。
JUnit是Java语言的比较著名的单元测试库,可以在Maven中加入以下依赖来引入:
1 |
|
开发习惯
在进行Java开发(其他语言应该也类似),会在放置源码的同级目录下放一个test
包,其中放着专门用于测试的代码。如果使用Maven进行项目构建,Maven也会帮你自动创建相关的包。
实际上,不用这么按部就班也可以将代码跑起来,但是这样可以让代码看起来更规范,就像main函数执行成功通常返回0一样。
程序入口
使用JUnit库进行测试时,不需要将main函数作为程序入口,只需要在你希望进行的单元测试函数前面加上@Test
注解即可,便可以在IDEA中直接运行这个单元测试。
断言
断言类似C中的assert
,当条件不满足时就会提前终止测试并提供相关信息。只不过JUnit5提供了多种封装,方便在多种数据之间进行比较,常用到的有:assertEquals
、assertNotEquals
、assertTrue
、assertThrows
等,用法也很简单,望文生义就可以知道它们的用途。
但是令人非常难以接受的一点是,这些函数都是Assertions
类的静态成员函数,使用时必须通过Assertions
类调用,IDEA似乎并不会自动从类中导入这些静态成员函数,所以如果图省事需要手动import
:
1 |
|
注解
注解类 | 作用 |
---|---|
@RepeatedTest(n) | 重复测试n次 |
@DisplayName(name) | 设置显示名称,``name`可以包含特殊字符 |
@BeforeEach/@AfterEach | 在每个测试前/后运行的函数(不能为静态函数) |
@BeforeAll/@AfterAll | 所有测试开始前/后运行的函数(必须为静态函数) |