给软件工程师的12条单体测试的建议
单体测试是敏捷软件开发的支柱之一。最早由Kent Beck提出,如今单体测试已经进入了很多组织的系统和核心。单体测试帮助工程师减少缺陷数目和调试时间并有助于开发更健康、更稳定软件。
在本文中我们将探讨软件工程师在任何编程语言或环境中都可以应用的12条单体测试建议。
1 使用单体测试管理风险
一个新手可能会问为什么我要写单体测试?的确,难道测试不是软件工程师都想外包给QA人员的枯燥的东西吗?这是一个在现代软件工程中不复存在的思维定势。软件团队的目标是开发出最高质量的软件。消费者和商务用户可能在80年代和90年代还能容忍“蹩脚”的软件。但随着大量库、web服务以及支持重构和单体测试的集成开发环境的出现,现在已经无法为软件的缺陷找借口了。
单体测试背后的思想就是为每个软件组件创建一套测试。单体测试使得持续软件测试(continuous software testing)成为可能,因为较之手工测试,重复运行它们很廉价。
在你的系统扩展的同时,单体测试也不断扩展。每个测试都是系统可以运行的保证。代码中的缺陷就意味着风险。利用单体测试集,工程师可以使得缺陷数目和未测代码的风险大大降低。
2 为每个重要组件写一个测试用例(Test Case)
在你开始写单体测试的时候,试问自己我需要些什么样的测试?
最初的动机可能是写一堆功能测试。比如,检查系统不能功能的测试。这是不对的。正确是做法是为每个重要组件创建一个测试案例(一套测试)。
每次测试的关注是单个组件。在每个组件内部寻找一个接口――该组件公开暴露的行为的集合。你必须为每个公开的方法写至少一个测试。
3 创建虚的测试用例(Test Case)和测试工具(Test Utilities)
对于任何代码都可能有所有测试都需要做的共通的东西。首先针对开发语言选择一个测试框架。比如,在Java界工程师会使用Junit(一个使用Java编写测试的简单但又强大的框架)。该框架带有Test Case类作为所有测试的基类。通过添加适用于环境的方法和工具,所有你的测试用例都可以共享这些共通的基础设施。
4 编写聪敏的测试
测试很耗时间,所以应该确保它们有效。好的测试通过最少可能的代码检测每个组件的核心行为。例如,没有必要为Java Bean setter和getter方法编写测试,因为这些会通过其它方法测试到。
反之,应该编写关注系统行为的测试。你没有必要做到面面俱到。创建那些进入脑海的测试并随时准备在重新回来时加入更多测试。
5 为每个测试准备干净的环境
软件工程师一直都关注效率,所以当他们听说需要给每个测试单独准备环境时他们很担心性能。但是正确地从头开始准备每个测试很重要。你最不想要的就是因为适用了其他测试的遗留数据而导致的测试失败。确保每个测试都合理设置并不要去担心效率。
当你有了一个所有测试的共通环境时你可以在测试基类中加入静态的设置代码块(static set up block)。
6 适用Mock对象进行有效测试
设置测试不是那么简单,而且有时候第一眼的感觉是不可能做到。例如,如果在代码中适用Amazon Web服务的代码,你如何在不影响实际系统的情况下在测试中模拟它呢?
有几种方法。你可以制造一些假数据并在测试中适用它们。如果系统有用户,就可以利用特殊的专门为测试设定的帐户集合。
针对上线系统运行测试是很危险的,假使某些东西出错或者你删掉了实际用户数据。另一种选择就是使用称之为Stub或Mock对象的假数据。
一个Mock对象实现了特定接口并返回预先确定的结果。例如,你可以为Amazon S3创建一个从本地硬盘读取文件的Mock对象。Mock对象在测试带有很多组件的复杂系统是很有用。对于Java,有一些框架可以帮助生成Mock对象,其中最有名是JMock。
7 在重构代码时重构测试
测试只有在你真正投入时才会给你回报。你不仅需要编写测试,你还需要确保它们是最新的。在你给一个组件加入一个新的方法时,你需要加入一个或多个相应的测试。正像你需要清理不用的代码。你也需要清理不再有用的测试。
单体测试在进行大规模重构的时候尤为重要。重构关注代码的持续修整以保证其正确。如果你移动代码并修改了测试,通过重新运行所有相关的测试就可以确保你没有在更改系统的过程中破坏任何东西。
8在修改缺陷前先写测试
单体测试是寻找缺陷的有效武器。当你发现你代码中的一个问题,在修改代码之前写一个可以暴露该问题的测试。这样如果同样的问题重新出现,测试就可以捕捉到。
这点在你无法一次写好面面俱到的测试的情况下非常重要。在你为一个缺陷加一个测试的时候,你也在以一种训练有素的方式填补原有测试的漏洞。
9 利用单体测试确保性能
除了确保代码的正确性之外,单体测试还可以确保你的代码性能不会随着时间变迁而退化。很多系统会随着变大而变慢。
为了编写性能测试,你需要在你的test class的基类中实现开始和结束函数。在适当的地方你可以使用时间相关的方法或代码并断言消耗的时间在期待性能的极限值之内。
10 为并发代码编写测试
并发代码很复杂并且通常是很多缺陷的根源。因此对并发代码进行单体测试很重要。进行的方法是通过使用休眠和锁系统(System of Sleeps and Locks)。在测试中你可以在需要等待特定系统状态的时候调用休眠代码。尽管这不是100%正确的解决,但在多数情况下这就足够了。为了在更复杂的环境中模拟并发,你需要在所测试对象之间传递锁。通过这些,你就可以以顺序执行的方式模拟并发系统了。
11 持续运行测试
测试的意义就在于可以不断运行它。特别对于数十人在同一个代码库上进行开发的团队中,持续的单体测试尤为重要。你可以将测试设置为每几个小时运行一次或者在每次代码签入时运行或者每天运行一次(通常在深夜)。可以根据项目确定一个最合适的方法并让测试可以自动持续运行。
12 享受测试!
可能最重要的建议就是享受这一切!当我第一次遇见单体测试时,我有过怀疑并认为这仅仅是增加工作量。但是我给了它一次机会,因为我信任的那个聪明的家伙告诉我它很有用。
单体测试让你的大脑进入了一个和编码状态很不同的状态。想着对于特定的组件什么才是简单和正确的测试集很有挑战性。
一旦你开始写测试,你就会想没有它们你会怎样?为了让测试更有趣你可以加入结对编程(Pair Programming)。无论你是和同事一切写测试代码还是帮对方写测试代码,都一定会很有趣。每天结束的时候,看着所有的测试都通过你就知道你的系统真的可以工作了,你会觉得很轻松。
请加入这个讨论!和我们一起分享来自你的项目的单体测试经验。
原文链接: http://www.readwriteweb.com/archives/12_unit_testing_tips_for_software_engineers.php