欢迎来到宝石工坊
在过去的几周里,我们看到了一些很棒的工具发布,用于
帮助 Ruby 程序员遵循测试优先原则开发程序,并且我将
在本文后面讨论其中三个。但首先,最近出现了一些发人深省的
电子邮件和博客文章出现在 Ruby 社区中,我想
在这里仔细看看其中的一些。
在 ruby-talk 邮件列表上,一个最受欢迎的定期活动是
Ruby Quiz,由
James Gray 协调。他每周五发布一个新的测验,经常使用
其他社区成员贡献的信息。然后,在
随后的星期四,他发布测验解决方案的讨论和总结。
在二月中旬,Gray 发布了 Quiz 67,一系列
元编程公案,鼓励了测试优先开发一个
用于对象和类的构建方法。完成
各种公案并阅读生成的消息对于
比平常更多的参与者来说是一件非常有趣的事情。
Zenspider 做了一个
预告 关于
multitest,是他 ZenTest 工具即将推出的一个新功能。如果你曾经
因为 Ruby 从一个版本到另一个版本的细微变化而被困扰,这
看起来像天赐之物。multitest 可以针对
每个调用上的多个 Ruby 版本运行你的测试套件,帮助你捕获问题
你通常看不到的问题。
一个即将到来的、应该引起你注意的事件是 Canada on Rails,第一个
致力于 Ruby on Rails 的会议。它将于温哥华,BC,
4月13日和14日举行。特邀演讲嘉宾包括 David Heinemeier Hansson、
David Black 和 Geoffrey Grosenbach。这应该是一个很好的机会
更深入地参与 Ruby on Rails 社区。
最后,许多 Ruby 和 Ruby on Rails 书籍即将
出版。我一直在通过早期访问
计划关注其中的一些,并且我对其中的两本特别兴奋,
Enterprise
Integration with Ruby 和
Ruby for
Rails。它们似乎都注定要成为重要的
任何 Ruby 程序员的库的一部分。
你的工具箱里有什么?
本专栏的深入报道重点关注用于测试优先开发的工具。如果
你不熟悉测试优先开发,并且想学习
更多,这里有一些好的资源
- XP 杂志
- 我在 IBM Developerworks 上的
文章 - 我之前在 ZenTest 上的文章
我喜欢使用测试优先的方式编写代码,因为我对
我编写的内容更有信心。使用测试优先,我可以快速完成一个可工作的
实现,并且可以轻松地重构为更好的设计。
Ruby 提供了很好的工具来按照
测试优先原则工作,或者说一些好的测试优先工具可用于
Ruby。
我的测试优先工具箱包括 Test::Unit、rake、rcov、unit_diff 和
autotest。前两个对于大多数 Ruby hackers 来说应该很熟悉。如果你
还不熟悉它们,Test::Unit 在
Pick
Axe 书和 www.ruby-doc.org 中有很好的文档记录。
你可以在
我的
IBM Developerworks 文章 和
这篇文章
中阅读更多关于 rake 的信息.
Martin Fowler 的
如果你还没有使用 Test::Unit 和 rake,花一些
时间来学习如何使用这些优秀的工具。Test::Unit 是
与 Ruby 一起分发,rake 可以作为 rubygem 从 RubyForge 获取。
至于我的工具箱中的其他工具,rcov 是一个代码覆盖率分析
工具。当针对单元测试套件运行时,它会生成调用覆盖率
对实现代码的分析。rcov 可以从 eigenclass.org 获取。你可以
生成 HTML 输出——看看 这里 的示例——或者 ASCII
输出,下面显示了它的截断形式。rcov 工作得相当
快;使用 rcov 运行程序只比正常运行程序慢两到三倍
正常运行程序的速度。此外,rcov 产生很好的,
class MockDB | 2 | 0 def exec(query, &block) | 11 case query.split(' ')[3] | 5 when 'zero' | 5 num = 0 | 1 when 'one' | 4 num = 1 | 1 else | 0 num = 2 | 3 end | 0 | 0 yield [num] | 5 | 0 end | 0 | 0 end | 0
有用的结果。
$ rcov test/test_hostname
你可以像这样针对测试套件运行 rcov
- 其他感兴趣的命令行选项包括
- -t:生成纯文本输出
- -T:生成花哨文本输出
- -p:生成性能分析输出
-x:排除文件;这个选项接受一个逗号分隔的列表,其中包含 - 正则表达式
--no-html:不要创建 HTML 文件
如果你选择生成文本输出,那么将输出重定向到
文件可能是值得的。或者,你可能想要使用tee
将其转储到一个文件——rcov 输出可能
变得很长。
虽然覆盖率工具通常用于显示你需要
编写更多测试的位置,但我最近有一次经历,rcov 引导我进行了一次
重构。我一直在使用测试优先的方式编写主机名检查代码,持续了
几个小时,并且已经实现了一些检查。我决定
从编写代码中休息一下,看看我的覆盖率有多好。我
期望它达到 100%,但有时很高兴看到这一点得到证实。
所以,我很震惊地看到绿色条的末端有一个红色条带。有些东西
没有被测试到!
我检查了代码和我的断言。我可以看到我在
测试失败的情况,但 rcov 不相信我。结果是
我在未覆盖的检查之后实现的检查重复了测试,
所以我的失败在到达那里之前就被捕获了。我需要要么
拆分我的检查方法,要么消除死代码。感谢
rcov,我的代码最终变得更小了一个方法。
当前版本的 rcov 确实有一个小 bug,你可能需要
注意。它不会查找在 “and” 或
“or” 语句后的行连续符。这是一个对 rcov 代码的单行修复,Mauricio
将在下一个版本中包含它。
回到我的工具箱中的其他工具,autotest 和 unit_diff 与,
ZenTest 一起分发,ZenTest 可以作为 rubygem 获取。它们都旨在帮助简化持续
测试到你的日常工作中。
如果你的代码在 ./lib 中,你的测试在 ./test 中,autotest 会吸取
测试文件,运行它们并显示
结果。它在测试文件和实现文件之间构建一个映射,
类和方法。每次你在映射中保存一个文件或创建一个
被放入列表中的文件时,它会重新运行测试。任何时候,当一个
测试失败时,autotest 会进入一个更紧密的循环,只运行失败的
测试。这允许你几乎立即捕获到失败的代码,并且
专注于纠正它。
在使用 autotest 工作了几个小时后,我才
进入了它的节奏。我用它做的第一件事是
小任务——我更新了一些 Ruby 1.8.4 的测试——所以 autotest 没有
时间来循环完成整个睡眠、扫描更改和运行
测试套件循环。然而,autotest 有一种处理这种情况的方法;
按一次 Ctrl-c 会立即重新运行测试。按下键
组合键两次会终止 autotest。
我还发现,当测试无法运行时,autotest 不喜欢它。
当我在使用测试优先的方式编写代码时,在开发的开始阶段
循环中,我经常需要一个不存在的实现文件。
在常规开发过程中,我很容易包含一个拼写错误或语法
错误。这些中的任何一个都会导致 autotest 打印 Ruby 生成的
错误消息,然后是一行显示
# Test::Unit died, you did a really bad thing, retrying in 10
这对你的自尊心不是最好的,但它是一种描述性的方式来抛出一个
红旗并让你保持在轨道上。
最后一个缺点:autotest 也不喜欢 Emacs 生成的自动保存
文件,并且在找到一个时崩溃。一个简单的补丁已经被发布到
ZenTest RubyForge 项目。一个新版本即将发布,所以
这个问题应该很快就会消失。
unit_diff 是另一个很快变得非常有用的小程序。它
将来自
失败的断言的 “expected” 和 “received” 部分通过 diff,从而将大块文本减少到
更易于管理的东西。unit_diff 的作者 Eric Hodel 说
该程序是为了帮助 ParseTree 开发而编写的,其中
任何失败的断言都可能产生多个屏幕的密集、难以阅读的
输出。unit_diff 将这种噩梦变成两到三行,这些行
显示确切的错误。此外,我发现它在处理
XML 输出时非常有用。
unit_diff 也很容易运行。只需通过管道将你的正常测试传递给它,
就像这样
$ ruby test/test_hostname |unit_diff
它发现的任何错误都会被捕获并适当地显示。
希望你喜欢这次对一些 Ruby 测试优先
开发工具的简单介绍。我很快会回来谈论更多 Ruby 主题。
如果你想看到我涵盖特定的主题,请随时
在这里留下评论。