开发者编写高质量测试的征途上可谓布满荆棘,数据库、中间件、不同的文件系统等复杂外部系统的存在,令开发者在编写、运行测试时觉得苦恼异常。由于外部系统常常运行在不同机器上或者本地单独的进程中,开发者很难在测试中操作和控制它们。外部系统以及网络连接的不稳定性(外部系统停止响应或者网络连接超时),将有可能导致测试运行过程随机失败。另外,外部系统缓慢的响应速度(HTTP访问、启动服务、创建删除文件等),还可能会造成测试运行时间过长、成本过高。种种问题使开发者不断寻找一种更廉价的方式来进行测试,mock便是开发人员解决上述问题时祭出的法宝。mock对象运行在本地完全可控环境内,利用mock对象模拟被依赖的资源,使开发者可以轻易的创建一个稳定的测试环境。mock对象本地创建,本地运行的特性更是加快测试的不二法门。
我所在团队设计开发的产品是持续集成服务器,产品特性决定了它需要在各个平台(Windows, Mac, Linux等)与各种版本管理工具(svn, mercurial,git等)、构建工具(ant, nant, rake等)进行集成,对于外部系统的严重依赖让我们在编写测试时遇到了很多困难,我们自然而然的选用了JMock作为测试框架,利用它来隔离外部系统对于测试的影响,的的确确在使用JMock框架后测试编写起来更容易,运行速度更快,也更稳定,然而出乎意料的是产品质量并没有如我们所预期的随着不断添加的测试而变得愈加健壮,虽然产品代码的单元测试覆盖率超过了80%,然而在发布前进行全面测试时,常常发现严重的功能缺陷而不得不一轮轮的修复缺陷、回归测试。为什么编写了大量的测试还会频繁出现这些问题呢? 在讨论之前先来看一个真实的例子:
我们的产品需要与Perforce(一种版本管理工具)进行集成,检测某段时间内Perforce服务器上是否存在更新,如果有,将更新解析为 Modification对象。将这个需求反应在代码中,便是首先通过Perforce对象检测服务器更新,然后将标准输出(stdout)进行解析:
public class PerforceMaterial { private List parseChanges(String output) { public class Perforce { |
相应的mock测试也非常容易理解:
..... context.checking(new Expectations() {{ List list = perforceMaterial.findModifications(start, end);//调用被测方法 assertThat(list.get(0).revision(), is("4")); |