第九章 单元测试
9.1 TDD三定律
- 在编写不能通过的测试代码之前,不能编写生产代码(业务代码);即先写不能通过编译的测试代码,再编写业务代码;
- 编写刚好无法通过的测试代码;即编写范围刚好的测试代码,无法通过是由于业务代码未编写;
- 编写范围刚好的业务代码,使测试刚好通过;
9.2 保持测试整洁
测试代码在业务单元代码之前编写,每一个业务单元对应一个测试;这导致测试代码和业务代码有相当的代码量;这就需要保持测试代码的整洁;
测试必须随生产代码的演进而修改;当测试越脏,越纠缠,就越会花更多的时间到测试代码上而不是生产代码;
当测试过于混乱时,不得不放弃测试;没有测试的生产代码就无法保证修改不会影响到系统的其他部分;故障率开始增加;
因此测试代码和生产代码一样重要
测试带来的好处:
- 测试让代码可扩展、可维护、可复用;
- 因为当代码修改时,测试可以及时发现问题的存在;
9.3 整洁的测试
整洁的测试要有非常好的可读性;
明确、简洁和足够的表达力;以尽可能少的文字表达大量的内容;
测试代码中应该减少重复,没有细节展现,以免影响阅读;
上图代码中充斥着addPage和assertSubString的重复;
此外代码中有过度的细节,使读者沉浸在细节中;
public void testGetPageHierarchyAsXml() throws Exception{ //build makePages("PageOne","PageOne.ChildOne","PageTwo"); //operate submitRequest("root","type:pages"); //check assertResponseIsXML(); assertResponseContains("<name>PageOne</name>","<name>PageTwo</name>","<name>ChildOne</name>"); } public void testSmbolicLinksAreNotInXmlPageHierarchy() throws Exception{ //build WikiPage page = makePage("PageOne"); makePages("PageOne.ChildOne", "PageTwo"); //operate addLinkTo(Page, "PageTwo", "SymPage"); submitRequest("root", "type:pages"); //check assertResponseIsXML(); assertResponseContains("<name>PageOne</name>","<name>PageTwo</name>","<name>ChildOne</name>"); assertResponseDoseNotContain("SymPage"); } public void testGetDataAsXml() throws Exception{ //build makePageWithContent("TestPageOne", "test page"); //operate submitRequest("TestPageOne", "type:data"); //check assertResponseIsXML(); assertResponseContains("test page", "<Test>"); }
每一个测试都遵循着BUILD-OPERATE-CHECK的结构进行编写,方便阅读,了解测试目的;
9.3.1 面向特定领域的测试语言
- 面向测试的语言并没有直接使用编写的API,而是进行了一层的包装;这样能方便的进行编写测试,便于阅读;
- 这样的结构不是一次写成的,是需要对代码进行重构的;
9.3.2 双重标准
- 有些事情可能只会在测试代码中做,而生产代码中可能不会做;所以内存或者CPU的效率在测试代码中可以稍微妥协,以提高代码的可读性;
9.4 每个测试一个断言
- 遵循given-when-then的约定;
- given – some context
- when – some actions is carried out
- then – a particular set of consequences should obtain
- 这里所说的就是BUILD-OPERATE-CHECK
- 如果可以将每个测试设计为单个断言(test),这非常好;
- 当一个单元测试中包含多个断言时,应使断言数量最小化;
每个测试一个概念
- 每个测试只测试一个概念;过多的测试概念会扰乱思路;
9.5 F.I.R.S.T
- 快速 Fast(F):测试应该足够快;如果测试过慢就不愿进行测试,最终导致代码腐败;
- 独立 Independent(I):测试应该相互独立,某个测试不应为下一个测试设定条件;以任何顺序运行测试条件也是可以的;
- 可重复 Repeatable(R):测试可以在任何环境中通过;比如生产环境,质检环境;
- 自足验证 Self-Validating(S):测试应该有布尔值输出;无论成败,可以方便检测。
- 及时 Timely(T):测试应该及时编写;单元测试应该恰好在使其通过的生产代码之前编写;