浏览 4445 次
|
该帖已经被评为精华帖
|
|
|---|---|
| 作者 | 正文 |
|
最后更新时间:2007-11-04 关键字: 测试
引文
这篇文章是7月份写的,但是10月份才发表。昨天看到了InfoQ的这篇介绍Rspec新特性的文章,才知道RSpec刚刚把本文所述功能实现了,所以本文那个matrix_spec插件基本废弃了。但是本文所讲主要是矩阵化测试的思想,对于RSpec中的RBehave特性的理解还是很有帮助的。 摘要对于Rails应用的自动化测试,本文介绍了如何使用矩阵测试来提高测试覆盖率,减少编写测试代码的工作量。同时介绍了如何使用正交化方法进行代码复用。文中使用的测试工具是RSpec,但是原理上Test::Unit 同样适用。引文本 文作者是一名“测试驱动强迫症”患者和“调试恐惧症”患者。以TDD为荣,以不写测试为耻。以100%测试覆盖率为荣,以低测试覆盖率为耻。全面覆盖的 Test Case都有这样的特点,一方面要覆盖所有的行为,另一方面要覆盖所有的边界条件,就是说每一个边界条件和每一个行为的组合都要测试到。这就给手工编写测 试带来了巨大的挑战,如何重用和缩减测试代码是令人头痛的问题。编写测试和编写实现代码的思维方式有很大差别。实现代码就像狙击枪,目标明确--通过当前失败的测试。测试代码就像轰炸机,对所有可能的目标进行地毯式轰炸,一旦有失败的测试用例,就是轰炸成功。轰炸机的弹药越充足,越便宜,轰炸成功的几率就越大。 如何用最廉价的方式得到最全面的测试用例呢?通过动态组合行为和边界条件,我们就可以生成所有可能的测试,从而最大限度地重用测试代码。这就是Matrix Test的思想。 矩 阵测试方法是由ZenTest的作者 Ryan Davis在今年4月提出的,并且在ZenTest 3.5.0中提供了一个Test:Unit的Matrix Test实现。矩阵测试的原理是把Test Case分解成3个正交的部分--行为,边界条件以及校验,通过矩阵来描述如何运行时生成所有可能的Test Case,从而使测试覆盖率最大化。 本文作者开源了一个针对RSpec的矩阵测试实现--matrix_spec,本文的Matrix Test是通过matrix_spec来实现的。 RSpecRSpec是Ruby语言的新一代测试工具,跟Ruby的核心库Test::Unit相比功能上和非常接近,RSpec的优点是可以容易地编写领域特定语言(Domain Specific Language,简称DSL)。RSpec 的一个重要目标是支持Behaviour-Driven Development (BDD) , BDD是一种融合了 Test Driven Development, Acceptance Test Driven Planning和Domain Driven Design的一种敏捷开发模型。两种测试代码的比较: Test::Unit assert_equal 2, post.comments.size RSpec post.should have(2).comments 可以看出RSpec的测试语法非常接近英语的自然语言,易于理解和维护。这种测试代码不仅仅是测试,更是用代码来撰写的Function spec。 例子
在边界条件很多的情况下,编写测试覆盖所有测试用例非常辛苦。比如对PostsController的 show 和 edit 两个 action 编写功能测试。并且需要测试两个边界条件,普通用户登录 VS 管理员登录。测试用例用文字描述如下: |
|
| 返回顶楼 | |
|
最后更新时间:2007-11-07
非常有启发性的文章。编写大量测试用例确实是很大的负担,而且还要不断去维护这些测试代码。
|
|
| 返回顶楼 | |
|
最后更新时间:2007-11-23
感觉如下图:
很强大,但不易用。 比较适合一些比较机械化的重复。但往往测试不是等所有需求出来再做的,而是分阶段进行。 如权限问题,第一天,客户说,要先注册,于是有了注册用户和非注册用户。第二天,客户又说,要有阶级,于是注册用户中又分类为普通用户和管理员。第三天,客户发现,注册用户间可以互相编辑对方的帖子(按找楼主的例子),于是客户说,要自主!于是各篇帖子有了主... 如以下代码,貌似有很多重复可以抽象出来,但似乎又不容易,谁来试试矩阵化一下?(比较臭,请自觉捂住鼻子)
# permit testing
# add by RainChen @ 2007-10-12 23:08
def test_should_admin_can_edit_any_posts
assert current_user.can_edit_post?(posts(:posted_by_normal))
assert current_user.can_destroy_post?(posts(:posted_by_normal))
assert current_user.can_edit_post?(posts(:posted_by_admin))
assert current_user.can_destroy_post?(posts(:posted_by_admin))
end
# add by RainChen @ 2007-10-12 23:26
def test_should_guest_can_not_edit_any_posts
logout!
assert !current_user.can_edit_post?(posts(:posted_by_normal))
assert !current_user.can_destroy_post?(posts(:posted_by_normal))
assert !current_user.can_edit_post?(posts(:posted_by_admin))
assert !current_user.can_destroy_post?(posts(:posted_by_admin))
end
# add by RainChen @ 2007-10-12 23:08
def test_should_normal_can_edit_his_posts
login_as :normal
assert current_user.can_edit_post?(posts(:posted_by_normal))
assert current_user.can_destroy_post?(posts(:posted_by_normal))
end
# add by RainChen @ 2007-10-12 23:26
def test_should_normal_can_not_edit_others_posts
login_as :normal
assert !current_user.can_edit_post?(posts(:posted_by_admin))
assert !current_user.can_destroy_post?(posts(:posted_by_admin))
end
# add by RainChen @ 2007-10-13 0:37
def test_should_normal_can_access_his_posts_editing_page
login_as :normal
get :edit, :id => posts(:posted_by_normal).id
assert_response :success
assert_template 'edit'
end
# add by RainChen @ 2007-10-13 0:37
def test_should_normal_can_not_access_others_posts_editing_page
login_as :normal
get :edit, :id => posts(:posted_by_admin).id
assert_response :redirect
assert_redirected_to :action => 'login'
end
|
|
| 返回顶楼 | |






