第九章 单元测试
编写单元测试是很有必要的
9.1 TDD三定律
测试驱动开发
- 在编写不能通过的单元测试前,不可编写生产代码
- 只可编写刚好无法通过的单元测试,不能编译也算不通过
- 只可编写刚好足以通过当前失败测试的生产代码
总的来说就是先写测试再写生产代码,写一个测试就应该立即写它的实现代码
9.2 保持测试清洁
测试代码和生产代码一样重要,它需要被思考、被设计和被照料。它该像生产代码一般保持整洁
单元测试让你的代码可扩展、可维护、可复用
9.3 整洁的测试
可读性、可读性、可读性
呈现:构造-操作-检验模式
构造测试数据、操作测试数据、检验操作结果 public void testGetPageHierarchyAsXml() { makePages("PageOne", "PageOne.ChildOne", "PageTwo"); submitReqeust("root", "type:Pages"); assertResponseIsXml(); assertResponseContains("<name>PageOne</name>","<name>PageTwo</name>"); }
9.3.1 面向特定领域的测试语言
打造一套包装API的函数和工具代码,这样就能更方便的编写测试
9.3.2 双重标准
测试代码应当简单、精悍、足具表达力
书中的例子:
这样写测试代码难以阅读 @Test public void turnOnLoTempAlarmAtThreashold () { hw.setTemp(WAY_TOO_COLD); controller.tic(); asssertTrue(hw.heaterState()); asssertTrue(hw.blowerState()); asssertFalse(hw.coolerState()); asssertFalse(hw.hiTempAlarm()); asssertTrue(hw.loTempAlarm()); } 不如 @Test public void void turnOnLoTempAlarmAtThreshold() { wayTooCold(); assertEquals("HBchL", hw.getState()); } @Test public void void turnOnCoolerAndBlowerIfTooHot() { tooHot(); assertEquals("hBchL", hw.getState()); } ......
效率不是很高,要提高效率可能要使用StringBuffer
public String getState() { String state = ""; state += heater ? "H" : "h"; state += blower ? "B" : "b"; state += cooler ? "C" : "c"; state += hiTempAlarm ? "H" : "h"; state += loTempAlarm ? "L" : "l"; return state; }
StringBuffer有点丑陋,即使是在生产代码中,假使代价较小,我们都会避免使用StringBuffer
即便计算机和内存资源都很有限。不过,测试环境大概完全不必做限制
有些事我们不会在生产环境做,但是测试环境做却完全没问题
9.4 每个测试一个断言
单个断言是个好准则,每个单个测试中的断言数量更应该最小化
每个测试一个概念
每个测试函数只测试一个概念
9.5 F.I.R.S.T
快速:测试应该够快,能快速的运行
独立:测试应该相互独立,某个测试不应为下一个测试设定条件,我们应该可以独立的运行每个测试
可重复:测试应当可在任何环境中重复通过
自足验证:测试应该有布尔值输出,不应该通过查看日志或者手工比对测试值是否相同,那如果是这样测试还有什么意义?
及时:测试应及时编写,单元测试应该恰好在使其通过的生产代码之前编写,如果再编写生产代码之后编写测试,你会发现生产代码难以测试,你可能会认为某些生产代码本身难以测试,你可能就不会去设计可测试的代码
9.6 小结
确实如作者所说,我们只是触及了这个话题的表面,测试其实很重要,它保证和增强了生产代码的可扩展性、可维护性和可复用性
总结
其实很多公司在写代码的时候没要求去程序员去写测试代码,但是其实写测试代码是很有必要的,下次我也准备专门写一个文章来搞一下单元测试