本文是代码质量保障第2讲,浅谈单元测试。单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。这是基础,所以围绕着单元测试,我从网上搜集和总结了相关的概念,以助你完善体系。
来自百度百科。
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。
使用单元测试可以有效地降低程序出错的机率,提供准确的文档,并帮助我们改进设计方案等等。
以下列举了一些我为什么使用单元测试的好处:
写单元测试的时机不外乎三种情况:
建议:我个人是比较推荐单元测试与具体实现代码同步进行这个方案的。只有对需求有一定的理解后才能知道什么是代码的正确性,才能写出有效的单元测试来验证正确性,而能写出一些功能代码则说明对需求有一定理解了。
单元测试不是越多越好,而是越有效越好!进一步解读就是哪些代码需要有单元测试覆盖:
被测系统(System under test, SUT)表示正在被测试的系统,目的是测试系统能否正确操作。根据测试类型的不同,SUT 指代的内容也不同,例如 SUT 可以是一个类甚至是一整个系统.
被测系统所依赖的组件,例如进程 UserService 的单元测试时,UserService 会依赖 UserDao,因此 UserDao 就是 DOC.
一个实际的系统会依赖多个外部对象, 但是在进行单元测试时,我们会用一些功能较为简单的并且其行为和实际对象类似的假对象来作为 SUT 的依赖对象,以此来降低单元测试的复杂性和可实现性。在这里,这些假对象就被称为 测试替身(Test Double)。测试替身有如下 5 种类型:
为 SUT 提供数据的假对象,我们举一个例子来展示什么是 Test stub.
假设我们的一个模块需要从 HTTP 接口中获取商品价格数据, 这个获取数据的接口被封装为 getPrice 方法. 在对这个模块进行测试时, 我们显然不太可能专门开一个 HTTP 服务器来提供此接口, 而是提供一个带有 getPrice 方法的假对象, 从这个假对象中获取数据. 在这个例子中, 提供数据的假对象就叫做 Test stub.
实现了简单功能的一个假对象。Fake object 和 Test stub 的主要区别就是 Test stub 侧重于用于提供数据的假对象,而 Fake object 没有这层含义.
使用 Fake object 的最主要的原因就是在测试时某些组件不可用或运行速度太慢,因而使用 Fake object 来代替它们.
用于模拟实际的对象, 并且能够校验对这个 Mock object 的方法调用是否符合预期。
实际上,Mock object 是 Test stub 或 Fake object 一种,但是 Mock object 有 Test stub/Fake object 没有的特性,Mock object 可以很灵活地配置所调用的方法所产生的行为,并且它可以追踪方法调用,例如一个 Mock Object 方法调用时传递了哪些参数,方法调用了几次等。
在测试中并不使用的,但是为了测试代码能够正常编译/运行而添加的对象。例如我们调用一个 Test Double 对象的一个方法,这个方法需要传递几个参数,但是其中某个参数无论是什么值都不会影响测试的结果,那么这个参数就是一个 Dummy object。Dummy object 可以是一个空引用,一个空对象或者是一个常量等。
简单的说,Dummy object 就是那些没有使用到的,仅仅是为了填充参数列表的对象。
可以包装一个真实的 Java 对象,并返回一个包装后的新对象。若没有特别配置的话,对这个新对象的所有方法调用,都会委派给实际的 Java 对象。
mock 和 spy 的区别是:mock 是无中生有地生出一个完全虚拟的对象,它的所有方法都是虚拟的;而 spy 是在现有类的基础上包装了一个对象,即如果我们没有重写 spy 的方法,那么这些方法的实现其实都是调用的被包装的对象的方法。
所谓 test fixture,就是运行测试程序所需要的先决条件(precondition)。即对被测对象进行测试时所需要的一切东西(The test fixture is everything we need to have in place to exercise the SUT)。这个 东西 不单单指的是数据,同时包括对被测对象的配置,被测对象所需要的依赖对象等。JUnit4 之前是通过 setUp, TearDown 方法完成, 在 JUnit4这, 我们可以使用@Before 代替 setUp 方法, @After 代替 tearDown 方法。
注意,@Before 在每个测试方法运行前都会被调用,@After 在每个测试方法运行后都会被调用。
因为 @Before 和 @After 会在每个测试方法前后都会被调用,而有时我们仅仅需要在测试前进行一次初始化,这样的情况下,可以使用@BeforeClass 和@AfterClass 注解。
在 JUnit 3中, 测试方法都必须以 test 为前缀,且必须是 public void 的,JUnit 4之后,就没有这个限制了,只要在每个测试方法标注 @Test 注解,方法签名可以是任意的。
通过 TestSuit 对象将多个测试用例组装成一个测试套件,测试套件批量运行。
通过@RunWith 和@SuteClass 两个注解,我们可以创建一个测试套件。通过@RunWith 指定一个特殊的运行器,即 Suite.class 套件运行器,并通过@SuiteClasses 注解,将需要进行测试的类列表作作为参数传入。