论坛首页 编程语言技术论坛

今天你BDD了吗?

浏览 18726 次
该帖已经被评为精华帖
作者 正文
   发表时间:2009-06-30   最后修改:2009-06-30
文档即测试?

首先看一下以下这段文字:
引用

Feature: Sign in
  In order to get access to protected sections of the site
  A user
  Should be able to sign in

    Scenario: User is not signed up
      Given no user exists with an email of "email@person.com"
      When I go to the sign in page
      And I sign in as "email@person.com/password"
      Then I should see "Bad email or password"
      And I should not be signed in     

    Scenario: User is not confirmed
      Given I signed up with "email@person.com/password"
      When I go to the sign in page
      And I sign in as "email@person.com/password"
      Then I should see "Your account is not active"
      And I should not be signed in

   Scenario: User enters wrong password
      Given I am signed up and confirmed as "email@person.com/password"
      When I go to the sign in page
      And I sign in as "email@person.com/wrongpassword"
      Then I should see "Bad email or password"
      And I should not be signed in

   Scenario: User signs in successfully
      Given I am signed up and confirmed as "email@person.com/password"
      When I go to the sign in page
      And I sign in as "email@person.com/password"
      Then I should see "Signed in successfully"
      And I should be signed in

   Scenario: User signs in and checks "remember me"
      Given I am signed up and confirmed as "email@person.com/password"
      When I go to the sign in page
      And I sign in with "remember me" as "email@person.com/password"
      Then I should see "Signed in successfully"
      And I should be signed in   
      When I return next time
      Then I should be signed in


第一印象就是一段关于用户登录的文档描述吧?
但实际上,这是一段实际项目中关于用户登录场景的集成测试“代码”

测试即文档?
准确来说,以上代码是在一个ROR项目中使用Cucumber进行BDD开发的一个场景描述(测试)。

什么是BDD?
http://en.wikipedia.org/wiki/Behavior_Driven_Development
这不是文本重点,有兴趣的同学可以自行搜索更多文章。

为什么要BDD?
说到BDD,就不得不提TDD(http://en.wikipedia.org/wiki/Test-driven_development)
TDD的开发流程:


Javaeye上有篇老帖子: 什么是“测试驱动开发” (http://www.iteye.com/topic/20063 ),点出了为什么要进行TDD的几个关键问题:
第一,自然语言的描述容易产生歧义
第二,不能自动化地验证
第三,不能保证文档与程序同步

TDD强调的是功能模块的测试,很多人都愿意使用单元测试,想尝试TDD,但都会有个问题,不知从哪里开始下手,而我们面向更多的是对一个场景的实现,因此BDD应运而生,更符合开发者的思维习惯。

做开发,不能没有文档。我们做开发时,一般都是先接到一个需求描述(简单文档),我们作为开发者,要做的工作就是实现这个功能(场景)的代码编写,例如用户注册、用户登录、忘记密码的流程。
而Cucumber就是为我们提供了这么一个工具,让我们能在用简单语言描述我们要做的事情同时,也顺带完成了测试代码的编写,剩下的就类似是做填空题一样,逐步编写实现代码,让所有测试都通过。当所有实现代码都出来了,一个完整的项目也就出来了(文档+代码+测试)。这也符合Rails DRY原则,不要做重复工作。


引用
“软件工程课讲得清楚,只有源代码的软件不能算软件,因为它不可理解、不可维护;源代码加上文档,才算是程序员完整地交付了自己的工作。”

引用自 http://www.iteye.com/topic/20063

我觉得还需要加上测试,才算是一个完整的项目。

怎么在Ruby on Rails 中怎么使用Cucumber进行BDD开发?
什么是Cucumber?
项目主页:http://cukes.info/
引用
Cucumber is Aslak Hellesøy’s rewrite of RSpec’s “Story runner”, which was originally written by Dan North. (Which again was a rewrite of his first implementation - “RBehave”). Early versions of “Story runner” required that stories be written in Ruby. Shortly after, David Chelimsky added plain text support with contributions from half a dozen other people. 

为什么选择Cucumber?
在 Rails 已经有一些BDD 工具: Rspec (http://rspec.info/)
使用的测试代码类似:
     describe MovieList do 
       context "when first created" do 
          it "should be empty" do 
            movie_list = MovieList.new 
            movie_list.should be_empty 
          end 
       end 
     end


虽然也很DSL,但还不够简明、直观,“代码”的味道还是很重。对比之下,反观Cucumber的测试“代码”,即使是团队中不会编程的角色也能看得明白在描述什么。如果团队足够NB,可以有一个负责程序设计 的leader专门负责把需求用Cucumber的features来描述,根据各人的能力和工作量,再分发给其下的负责编码的开发人员进行实现的编码。

Cucumber的开发过程

(from http://www.pragprog.com/titles/achbd/the-rspec-book)

具体过程,7个步骤:
引用

1: Describe behaviour in plain text
2: Write a step definition in Ruby 
3: Run and watch it fail 
4: Write code to make the step pass 
5: Run again and see the step pass 
6: Repeat 2-5 until green like a cuke
7: Repeat 1-6 until the money runs out 


简单来说就是:写用例->跑测试->看结果->写实现->换个用例,再来一次

具体安装设置细节略过,Cucumber新手上路可以看视频:
http://railscasts.com/episodes/155-beginning-with-cucumber
或者文字版:
http://asciicasts.com/episodes/155-beginning-with-cucumber

Cucumber能做什么?
看看用Cucumber可以描述、测试多复杂的场景:
测试发邮件?没问题:


甚至可以把一个用户进行填写注册表单、登录自己的邮箱、查收激活邮件、点击激活链接的整个故事流程都跑起来:


又一个常见场景:
已注册用户忘记密码,然后通过点击“忘记密码”链接,系统发送激活链接到用户邮箱,用户通过打开邮箱中的更改密码链接,进入新密码修改页面,修改新密码后能成功登录:


用cucumber features -n 命令跑测试的效果:


一些基本技巧:
在其他场景中,经常会用前面测试过的“一个注册并激活的用户”,每次都copy一次?
Don't repeat yourself!
此时就可以在Cucumnber的steps中把上述过程都封装成一句话:
Given I am a confirmed user and signed in as "tom@iamtom.com" with password "mypassowrd"


实际的封装steps代码是:


第一步是新建一个已激活的用户,第二步是用这个用户来进行登录操作,这样就得到了一个“注册并已激活,已经成功登录的用户”

看看另一个测试用例,也是比较“经典”的在一个项目中新建一个todo item的场景:


其中
Then the project which name is "A trip" should has a todo which name is "To meet David"

的steps实现为:


通过这样的封装,可以用来断定任意两个model数据对象是否关联
可通用的描述语句为:
Then the {modelA} which {attr} is "{A}" should has a {modelB} which {attr} is "{B}"


一些进阶技巧:
http://railscasts.com/episodes/159-more-on-cucumber
示范如何做通过设置模板句法来解决一些重复、相似的features编写技巧

如:
Feature: Manage Users
        In order manage user details
        As a security enthusiast
        I want to edit user profiles only when authorized

  Scenario Outline: Show or hide edit profile link
    Given the following user records
      | username | password | admin |
      | bob      | secret   | false |
      | admin    | secret   | true  |
    Given I am logged in as "<login>" with password "secret"
    When I visit profile for "<profile>"
    Then I should <action>

  Examples: 
    | login | profile | action                           |
    | admin | bob     | see "Edit Profile"     |
    | bob   | bob     | see "Edit Profile"     |
    |       | bob     | not see "Edit Profile" |
    | bob   | admin   | not see "Edit Profile" |

4 scenarios
16 passed steps


但我个人不推荐过多的使用这种技巧,我认为Cucumber的features就应该保持简明易懂,去除代码的味道。features里套用了模板,人阅读时就需要用脑“即时编译”,进行循环、替换,这就又会给缺乏编程经验的人作为功能描述文档阅读时造成障碍,削弱了“测试即文档”的作用。DRY不是圣旨,适当的重复并不是罪恶。
另外,在cucumber中使用webrat进行测试时,还可以搭配使用Selenium 来测试JS,十分强大,这会是我们下一步的尝试。
感受:
当我第一次看到Cucumber的测试代码,第一个感觉就是这真的是“代码”吗?很奇幻的感觉。
现在感觉Cucumber中的features的本质无非是一些用关键词堆砌的文本段落,然后一个用正则替换的方法来匹配替换真正的测试语句, 很简单的手段,但最重要的背后的思考,它是bdd所强调一种开发风格、思维方式。
在用Cucumber开发了一段时间后,完全没有感觉不适或强迫感,每次看到测试通过后的一片绿色,心情就很轻松愉快。能很明确每一步在做什么,而且做完修改后,都能知道修改有没副作用。Ruby之父说用ruby开发是happy coding,开始以为just joking,现在真的有这种感觉,现在用Cucumber进行BDD则是多了一个项:happy testting
疑问?
I18n怎么处理?

Rails 有了Cucumber后,还需要单元测试吗?
http://www.pathf.com/blogs/2009/06/cucumber-rocks-but-its-not-a-replacement-for-unit-tests/

关于这个问题,我和同事都认为是需要的,只是在一些细节上有争议。

我在实际进行项目开发时,都是针对一个应用场景、案例、用户故事的思路来进行代码编写的,只有当流程跑通了(features 的测试通过),下一步才是保证这个流程在一些异常情况下也能得到预期的结果(用单元测试来保证,如更多的边界测试),简单来说是就 setup features->coding->unit test

同事认为,应该在写完cucumber的测试代码后,再写单元测试代码,最后再写实现代码,简单来说是 setup features ->unit test->coding

我不清楚到底那种才是正确的BDD流程,这需要继续探讨。

以上是我一个月来尝试BDD的经验分享,作为抛砖,其中或许有片面或错误的见解,望有同学们不吝指教、一起探讨。

有研究表明,三个星期可以改变或养成一个习惯,那你今天开始BDD了吗?

相关链接:
What is Test-driven Development?
http://edn.embarcadero.com/article/29690

Behavior Driven Development Using Ruby
http://www.oreillynet.com/pub/a/ruby/2007/08/09/behavior-driven-development-using-ruby-part-1.html
http://www.oreillynet.com/pub/a/ruby/2007/08/30/behavior-driven-development-using-ruby-part-2.html

Introduction to BDD with Cucumber
http://www.engineyard.com/blog/2009/cucumber-introduction/

A NEW LOOK AT TEST-DRIVEN DEVELOPMENT
http://blog.daveastels.com/files/BDD_Intro.pdf


本文PDF版(带排版):
http://www.iteye.com/topics/download/69f11949-58ed-33dd-8307-884d280ebad5
  • 大小: 37.5 KB
  • 大小: 23.4 KB
  • 大小: 68.3 KB
  • 大小: 76.5 KB
  • 大小: 103.8 KB
  • 大小: 51.5 KB
  • 大小: 56.8 KB
  • 大小: 47.4 KB
   发表时间:2009-06-30  
文章不错。BDD应该也会慢慢受到国内用户重视。
0 请登录后投票
   发表时间:2009-07-01  
不错,现在一直在用Cucumber

直接支持中文场景描述,而且可以使用场景大纲+例子的方式测试同一的用例不同条件的情况
0 请登录后投票
   发表时间:2009-07-01  
BDD在rails社区倒是非常流行,从某种程度上来说,也是得益于rails很方便得能够DSL带来得。
0 请登录后投票
   发表时间:2009-07-01  
这就是我想要的开发模式。

曾经在团队中用过这种模式,当时是以user story驱动。一个user stroy就类似这里的behavior。效果很好,能让大家能充分明白开发任务和入手点。

不知道这些behavior是如何组织和管理的,是否能全部串联起来成为一个真正完整的用例描述文档。
0 请登录后投票
   发表时间:2009-07-01  
个人认为 setup features ->unit test->coding  是比较正确的流程。看了很多教程包括railscasts里面都是用这个流程,实践起来也蛮畅快的。
发现一开始使用BDD的时候不知道从哪入手,写了很多蹩脚的测试用例,搞得生产力很低下。后面看了很多教程后找到感觉就顺畅了,重构起来心里也比较有底.
0 请登录后投票
   发表时间:2009-07-01  
genki 写道
个人认为 setup features ->unit test->coding  是比较正确的流程。看了很多教程包括railscasts里面都是用这个流程,实践起来也蛮畅快的。
发现一开始使用BDD的时候不知道从哪入手,写了很多蹩脚的测试用例,搞得生产力很低下。后面看了很多教程后找到感觉就顺畅了,重构起来心里也比较有底.


我是这样考量的,因为实际开发时,其实很多需求都是模糊不定的,有些细节问题只有把流程实现出来后才能发现,需求可能又会变动了,所以我才延迟了ut的编写。我是等流程完全确定了,才开始编写一些关键的UT保证边界值和一些可预见的异常测试。
0 请登录后投票
   发表时间:2009-07-01  
rainchen 写道
genki 写道
个人认为 setup features ->unit test->coding  是比较正确的流程。看了很多教程包括railscasts里面都是用这个流程,实践起来也蛮畅快的。
发现一开始使用BDD的时候不知道从哪入手,写了很多蹩脚的测试用例,搞得生产力很低下。后面看了很多教程后找到感觉就顺畅了,重构起来心里也比较有底.


我是这样考量的,因为实际开发时,其实很多需求都是模糊不定的,有些细节问题只有把流程实现出来后才能发现,需求可能又会变动了,所以我才延迟了ut的编写。我是等流程完全确定了,才开始编写一些关键的UT保证边界值和一些可预见的异常测试。

流程不是都应该在写 cucumber的 feature 时侯就确定下来了吗.
0 请登录后投票
   发表时间:2009-07-01  
genki 写道
rainchen 写道
genki 写道
个人认为 setup features ->unit test->coding  是比较正确的流程。看了很多教程包括railscasts里面都是用这个流程,实践起来也蛮畅快的。
发现一开始使用BDD的时候不知道从哪入手,写了很多蹩脚的测试用例,搞得生产力很低下。后面看了很多教程后找到感觉就顺畅了,重构起来心里也比较有底.


我是这样考量的,因为实际开发时,其实很多需求都是模糊不定的,有些细节问题只有把流程实现出来后才能发现,需求可能又会变动了,所以我才延迟了ut的编写。我是等流程完全确定了,才开始编写一些关键的UT保证边界值和一些可预见的异常测试。

流程不是都应该在写 cucumber的 feature 时侯就确定下来了吗.

这个太理想化了。
0 请登录后投票
   发表时间:2009-07-01  
这是我在这读到的比较有意思的东西~~~~
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics