`
angelguo
  • 浏览: 119841 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Ruby 行为驱动设计 (RSPEC)

    博客分类:
  • Ruby
阅读更多
你可能已经注意到,验证模对象改变了你在单元测试中执行的那类测试的本质。传统的单元测试中,通过断言(Assertion)来进行大多数的验 证。这是一种对应用程序状态的测试。模对象的测试,是测试测试中调用过程十分符合预期。它测试的是应用程序的行为。至少潜在能力上,行为测试更容易将期望 行为与实际的实现方法分开。



行为驱动设计(BDD)的口号是试图将测试设计从实现空间移到问题空间。部分地使用设计 BDD工具集,测试可以通过指定更接近自然语言的关键词来进行。BDD工具充分利用模对象,既作为说明问题的方法,也作为区分单独的个别单元测试的方法。 使用传统的TDD测试,低层方法的修改会导致很多测试失败。BDD认为一个代码的改变能产生许多测试失败表明这个测试不是真正意义上的单元测试,而是集成 测试,尽管是非常小的范围内。TDD方式的测试只有在实际对象不能得到或者不好用的情况下才会使用模对象,BDD方式则更加积极地使用模对象来将测试中的 函数与系统的其它部分分离。



这个章节将概要地介绍一下RSpec(Ruby中最流行的BDD测试包)。RSpec与Rails集成得非常好,包括单独地测试Controller, View和Helper函数的能力。



安装RSpec



RSpec即可以作为Ruby gem也可以作为Rails plugin。在Rails里面使用时,可以使用下面的命令同时安装RSpec plugin和RSpec Rails plugin。



Ruby代码
ruby script/plugin install -x svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec 
ruby script/plugin install -x svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec_on_rails

ruby script/plugin install -x svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec
ruby script/plugin install -x svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec_on_rails


通常,如果你不想将RSpec作为扩展组件安装到你的版本控制服务器中的话,你可以将-x选项去掉。



Windows用户还需要为RSpec安装一个叫win32console的gem来更好的运行RSpec。



在安装好RSpec之后,你需要运行下面命令来生成RSpec的路径与文件。



Ruby代码
ruby script/generate rspec 

ruby script/generate rspec

这 个命令的最主要的目的是在Rails的根目录下创建一个spec子目录,里面有一个类似标准test_helper.rb文件的 spec_helper.rb。它还创建了一些脚本,另一些文件你目前还用不着。然而,这个命令不会创建符合Rails命名规则的子目录。你需要手工来 做。



RSpec使用Rails现有的命名规则。下面的表格可以看出它通过简单的名字来命名的。



Test Type            Example Name

Controller test     spec/controllers/reciper_controller_spec.rb

Helper test          spec/helpers/reciper_helper_spec.rb

Model test           spec/models/reciper_spec.rb

View test             spec/views/recipe/new_spec.rb



在这个章节中,由于我们已经生成了Model, View, Controller和Helper文件,所以我们将要手工创建这些规格文件。

如 果你是用RSpec新建项目的话,可以使用: rspec_controller, rspec_model和rspec_scaffold这3个RSpec自定义的生成器。它们和原来的生成器形式相同,使用一样的参数。唯一的区别是在 spec目录下创建了RSpec的原始测试文件,而不是test目录下的Test::Unit文件。



你可以使用 rake spec命令来运行所有的RSpec规格。如果你只想运行其中的一套,你可以用更明确的命令,例如:rake spec:models。一共有:controllers, helpers, models, plugings和views几个子任务。如果你安装了rcov,所有命令后面都可以加上“:rcov”来生成一个覆盖报告(用gem install rcov来安装rcov)。但是,就算有为view模板写RSpec测试,rcov也不会为ERB模板生成覆盖报告。在Rails 2.0中,默认的rake任务会同时运行test目录下的测试和spec目录下的RSpec规格。



写RSpec规格



一 个RSpec规格文件包含一个和多个行为,每一个包含有一个或多个例子。注意,命名方式已经表明了RSpec与Test::Unit之间的区别:"行为" 和"例子"讨论的是测试中功能和期望的概念,而"测试"和"断言"则关注实现。一个RSpec行为大约与一个Test::Unit类等价,虽然你经常可以 看到在一个规格文件中有多个行为,而很少看到一个Test::Unit类文件中有多个类。



描述一个行为的方法是:describe,而描述一个例子的方法是:it。一个规格文件的框架大概是这个样子:



Ruby代码
describe Foo do
  it “should not crash when I call it” do
    # do something testable here 
  end
end

describe Foo do
  it “should not crash when I call it” do
    # do something testable here
  end
end
可以注意到这个例子是用自然语言来描述的而不是用函数名。



在 一个行为中,你可以通过"befort"和"after"函数来指定初始环境和清除。这些函数有两个用法。默认的是“:each”,表示每一个单独的例子 运行之前或之后将要运行的代码块,就像在Test::Unit里面的setup和teardown函数一样。另一个用法是":all",表示在所有例子运 行前后所有例子完成之后运行的代码块。你可以用同样的方法指定多个before和after块,所有的块都会在合适的时间执行。



在一个行为中,任何用Ruby普通的def关键字声明的函数可以被所有行为中的例子使用,这样你就可以写一些自定义的验证函数和通用函数。



并且,虽然例子通常有一个代码块,但是你也可以临时仅仅为例子指定一个字符串而不提供代码块,例如:



Ruby代码
it “should do something that hasn’t been implemented yet” 

it “should do something that hasn’t been implemented yet”

RSpec将把这个测试标示为待完成,并且报表会将待完成的数量从通过数量或失败数量中分离出来。



你也可以再进一步,有一段代码,你知道会出错的测试,但是还是希望测试通过,通常的样子是这样的:



Ruby代码
it “should fix this silly bug” do
  pending(“this is Bob’s problem”) do
    #specify the failing test here 
  end
end 

it “should fix this silly bug” do
  pending(“this is Bob’s problem”) do
    #specify the failing test here
  end
end 
在这个用例里,RSpec将运行pending块中的代码,如果失败,将报告测试待完成,如果成功,将得到一个预期的失败,告诉你这个测试不再失败,你不需要用pending了。



写Model测试



接下来的几个部分,你将看到一些基于已经写的Test::Unit测试上RSpec规范的一些例子。测试目标因为已经写过,这里不再完全重新说明,我们着重于看看RSpec的测试是怎样工作的,并看看它们与Test::Unit之间的区别。我们先看看Model。



为了能让这些测试运行,需要将test/fixtures里的YAML文件复制到spec。下面的代码是从“spec/models/recipe_spec.rb”复制过来的:



Ruby代码
require File.dirname(__FILE__) + ‘/../spec_helper’ 
         
describe Recipe, “basic test suite” do
  fixtures :recipes
  fixtures :ingredients
         
  it “should have ingredients in order” do
    subject = Recipe.find(1) 
    subject.ingredients.collect { |i| i.order_of }.should == [1, 2, 3] 
  end
end 

require File.dirname(__FILE__) + ‘/../spec_helper’
      
describe Recipe, “basic test suite” do
  fixtures :recipes
  fixtures :ingredients
      
  it “should have ingredients in order” do
    subject = Recipe.find(1)
    subject.ingredients.collect { |i| i.order_of }.should == [1, 2, 3]
  end
end 
这个几乎是从第一章的单元测试直接翻译过来的。describe块建立了行为(还没有before和after代码)。it快建立了一个指定的期望:这个用例,我们期望ingredient成员总是排序的。



should函数(和他的兄弟should_not)是RSpec中测试状态的关键字。它后面紧接"=="。这个方法用来执行一个断言,右边的值是希望的值,左边的值(接在should之后)是实际的值。



你 可以在should之后接任何东西。特别的,任何be_<something>形式的语句都会自动地被RSpec翻译 成<something>?。因为nul?在所有对象中都被定义,你总是可以用should be_nil和should_not be_nil来测试。在一个Rails项目中,你总是可以测试should be_blank;数组可以测试should be_empty;等等。如果你想更方便阅读,你可以加上be_a或者be_an前缀。RSpec也非常聪明,可以在have/has之间做调整,这样一 个hash的should have_key将测试has_key?术语。记住,我们这里说的都是在名字中用了问号的方法,即使你在RSpec中不使用问号。



第 一个测试确实与原来的单元测试类似,下一个测试显示出更多RSpec与Test::Unit之间的区别。在第一章中,我们写了一堆测试来验证一个 Ingredient对象中解析一个像"2 cups of carrots, chopped"这样字符串的代码。还有一个测试来验证一个recipe能接受很多这样字符串,并且将它们转换成一个ingredient的列表。用 RSpec写的的recipe的测试如下:



Ruby代码
it “should split ingredient strings into separate lines” do
    Ingredient.should_receive(:parse).exactly(3).times.and_return do |s, rec, ord| 
      Ingredient.new(:recipe_id = >  rec.id,rder_of = >  ord, :amount = >  2, 
          :ingredient = >  ord) 
    end
    subject = Recipe.find(2) 
    subject.ingredient_string = 
        “2 cups carrots, diced\n\n1/2 tablespoon salt\n\n1 1/3 cups stock” 
    subject.ingredients.count.should == 3 
    subject.ingredients.collect { |i| i.order_of }.should == [1, 2, 3] 
    subject.ingredients.collect { |i| i.ingredient }.should == %w[1 2 3] 
end 

it “should split ingredient strings into separate lines” do
    Ingredient.should_receive(:parse).exactly(3).times.and_return do |s, rec, ord|
      Ingredient.new(:recipe_id = >  rec.id,rder_of = >  ord, :amount = >  2,
          :ingredient = >  ord)
    end
    subject = Recipe.find(2)
    subject.ingredient_string =
        “2 cups carrots, diced\n\n1/2 tablespoon salt\n\n1 1/3 cups stock”
    subject.ingredients.count.should == 3
    subject.ingredients.collect { |i| i.order_of }.should == [1, 2, 3]
    subject.ingredients.collect { |i| i.ingredient }.should == %w[1 2 3]
end 
测试中有两个部分。为recipe对象提供了一个包含3个ingredient和两个空行的字符串。我们假定recipe会忽视空行而解析其他行。



这 Test::Unit的测试中,调用了ingredient的parser函数,并且指定了用来得到期望解析结果的recipe中的对象。在RSpec的 测试中,你不需要调用ingredient的parser函数,因为你不需要测试它。取而代之的是第一行中,你你创建了一个Ingredient类的临时 模对象,返回一个虚拟的ingredient对象,并且断言它将正好被调用3次。这个虚拟的ingredient对象并不比较解析器实际的输出(它们仅仅 为了不产生nil异常而创建了最小限度的数据),但是你完全不必关心:解析器是否正确会在后面的解析器的测试中进行。这个测试仅仅是测试recipe是不 是能够接受有空行的数据,你不必关心解析器是不是正确。这里的关键是关注的测试中的实际函数并且为这个函数建立一圈围墙来让它完成测试。



RSpec 使用它自己的模对象规范,和FlexMock类似。(如果你已经使用过FlexMock或者其他Ruby的模对象工具,类似地配置RSpec很简单)。模 规范should_receive与FlexMock的模认证规范很类似。它可以用once, twice, times(n), at_least和at_most参数来指定所有的值。除了and_return,你还可以用带异常参数的and_raise来验证在错误环境下测试得到 的异常。用and_yield来指定一个代码块参数,为不是一个值。



在这个测试中使用的模定义了一个动态部分。因为你 为and_return设置了一段代码参数,所以你可以为通过设置参数,得到指定recipe id和order值的ingredient对象。虽然通常使用静态无互联的模对象比较好,在这个用例中有一个需要被验证的关联关系。ingredient 的order不是在parser自身设置的,而是通过recipe对象参数传进来的。如果ingredient的order是在模对象中静态设置的,就没 有办法验证order是不是真的被设置了。因此,像上面的代码,这个测试允许通过设置参数来动态设置order,并且为每个ingredient设置指定 的order,这个就可以完全验证recipe的order。



上面的测试是说明RSpec怎样让你可以将行为测试和状 态测试混合起来的好例子。RSpec也可以将函数名处理成易于阅读的形式。当我开始为ingredient解析器写集成测试的时候,我决定完全利用这一优 势。我在“spec/model/ingredient_spec.rb”中编写了以下代码:



Ruby代码
require File.dirname(__FILE__) + ‘/../spec_helper’ 
         
class String
  def parsing_to?(hash) 
    expected = Ingredient.new(hash) 
    actual = Ingredient.parse(self, Recipe.find(1), 1) 
    actual == expected 
  end
end
         
describe Ingredient, “basic parsing suite” do
  fixtures :ingredients, :recipes
         
  it “should parse a basic string” do
    “2 cups carrots, diced”.should be_parsing_to(:recipe_id = >  1, 
       rder_of = >  1, :amount = >  2, :unit = >  “cups”, 
        :ingredient = >  “carrots”, :instruction = >   “diced”) 
  end
end 

require File.dirname(__FILE__) + ‘/../spec_helper’
      
class String
  def parsing_to?(hash)
    expected = Ingredient.new(hash)
    actual = Ingredient.parse(self, Recipe.find(1), 1)
    actual == expected
  end
end
      
describe Ingredient, “basic parsing suite” do
  fixtures :ingredients, :recipes
      
  it “should parse a basic string” do
    “2 cups carrots, diced”.should be_parsing_to(:recipe_id = >  1,
       rder_of = >  1, :amount = >  2, :unit = >  “cups”,
        :ingredient = >  “carrots”, :instruction = >   “diced”)
  end
end 
其实这个例子中唯一特别的地方就是我为String类增加了一个函数。这也正是那些正统工程师在学习到Ruby可以为已有类随意增加函数时感到紧张的地方。



不 管怎样,因为这个函数只在测试中使用,我想就编程稳定性的观点来看还是没问题的。而且经过RSpec的神奇名字处理,“"2 cups carrots, diced".should be_parsing_to”这样的描述确实是更加丰富而易懂。实际上,我觉得是不是有必要为程序本身添加一个 String#parse_to_ingredient函数,不过先就这样吧。



写控制器的规格书




RSpec 在将controller, view和helper分开各自测试的处理上与标准的Rails测试工具比起来有很多优势。控制器的测试放在"spec/controllers"目录 下。这里为RecipesController规格书开了个头,我写了验证index函数的运行情况的测试,然后通过HTML的GET方法调用new函 数。第一个部分通过before(:each)方法来创建一个recipe的模对象。为了单独测试Controller而不使用Model和数据库,你需 要使用RSpec桩来阻止ActiveRecord类的new或者find函数的调用,这样可以不返回实际的ActiveRecord对象,而是返回模对 象。最后,你可以将所有代码放在“spec/controllers/recipes_controller_spec.rb”里面:



Ruby代码
require File.dirname(__FILE__) + ‘/../spec_helper’ 
         
describe RecipesController do
         
  before(:each) do
    @recipe = mock(“person”) 
    @recipe.stub!(:new_record?).and_return(false) 
    Recipe.stub!(:new).and_return(@recipe) 
    Recipe.stub!(:find).and_return(@recipe) 
  end  
  ... 
end

require File.dirname(__FILE__) + ‘/../spec_helper’
      
describe RecipesController do
      
  before(:each) do
    @recipe = mock(“person”)
    @recipe.stub!(:new_record?).and_return(false)
    Recipe.stub!(:new).and_return(@recipe)
    Recipe.stub!(:find).and_return(@recipe)
  end
  ...
end
RSpec的桩的用法与FlexMock方法有些小区别。代码设置了Recipe#new和Recipe#find的桩,并且前面两句代码让它们返回作为桩的recipe实例。



第一个规格说明了当用户执行index函数请求时所希望得到的结果:



Ruby代码
it “should get an index when requested” do
  get “index” 
  response.should be_success 
  assigns[:recipes].should_not be_nil 
end 

  it “should get an index when requested” do
    get “index”
    response.should be_success
    assigns[:recipes].should_not be_nil
  end 
这 个规格里面显示了一些RSpec用来测试控制器的结果的特殊函数。如果响应返回状态码200的话,should be_success将返回true。相类似的response.should be_redirect将测试重定向的状态。值得说一下,即使你没有为Controller写相关的View,测试should be_success也不会失败。



测试Controller是否正常渲染了View用带有希望被调用的模板路径作为参 数的should render_template来测试,如果Controller只是返回纯文本,可以用should have_text来验证它的文本内容。如果你要验证重定向,你可以用should redirect_to来验证,它可以指定一个完整的URL或者一个URL的本地路径,或者用url_for加上一组Hash选项。



最后一行代码用assigns的Hash,它类似于标准功能测试里面的assigns函数,它表示Controller创建的变量实例。你也可以访问flash和session等Hash,这样你可以验证Controller中的变量。



第二个Controller的测试像这样检查new函数:



Ruby代码
it “should respond to GET new with a captcha” do
   @token = mock_model(Token) 
   captcha = mock(MathCaptcha) 
   MathCaptcha.should_receive(:create).with(3).and_return(captcha) 
   get “new” 
   assigns[:captcha].should == captcha 
end 

it “should respond to GET new with a captcha” do
    @token = mock_model(Token)
    captcha = mock(MathCaptcha)
    MathCaptcha.should_receive(:create).with(3).and_return(captcha)
    get “new”
    assigns[:captcha].should == captcha
end 
如果你与传统的单元测试相比,原来的单元测试中用了大量的assert_select来验证View中的结果,而在RSpec中将在View的测试中来验证这些,这里你只需要测试Controller创建的变量或者数据库操作。



下面对HTTP的PUT的更新操作的测试显示了RSpec规格与单元测试之间的区别。



Ruby代码
it “should respond to a PUT with an update” do
  @recipe.should_receive(:update_attributes).with( 
      {“title” = >  “Grandma’s Chicken Soup”}).and_return(@recipe) 
  put “update”, :id = >  1, :recipe = >  {:title = >  “Grandma’s Chicken Soup”} 
  response.should redirect_to(“http://test.host/recipes/#{@recipe.id}”) 
end 

  it “should respond to a PUT with an update” do
    @recipe.should_receive(:update_attributes).with(
        {“title” = >  “Grandma’s Chicken Soup”}).and_return(@recipe)
    put “update”, :id = >  1, :recipe = >  {:title = >  “Grandma’s Chicken Soup”}
    response.should redirect_to(“http://test.host/recipes/#{@recipe.id}”)
  end 
这 个例子很简单,第一行建立了唯一一个recipe模对象,它表示我们希望得到一个update_attibutes调用。第二行执行了一个更新调用,第三 行验证了重定向到一个指定的URL上。同样,什么不用测试是很重要的:我们在这里不需要测试Recipe在调用update_attibutes之后是不 是真的更改了对应的属性,这里只测试Controller相关的行为。



说明View的行为



要 是说关于RSpec我还有什么需要讲的,那就是用一个RSpec例子来说明怎样通过努力地封装函数来测试系统的其他部分。所以你也不用感到惊奇,我们可以 将View从Controller和数据库中分离出来测试。下面的例子我将给你说明一下new.html.erb是怎样渲染面板的。实际上View的大多 数工作是在_from子模板中完成的。所以规格应该针对子模板来写。但是,实际上我假设我在写测试的时候还没有想好用子模板来处理,所以我写的规格书是针 对整个视图的。



这个面板上面有一个逻辑点,如果指定了MathCaptcha对象,那么将显示这个对象,否则不显示。另外,需要创建一个Recipe模对象和一个User模对象来进行测试。



下面的代码在spec/views/recipes/new_spec.rb文件中,显示了怎样创建模对象:



Ruby代码
require File.dirname(__FILE__) + ‘/../../spec_helper’ 
         
describe ‘recipe/new’ do
         
  before(:each) do
    @recipe = mock_model(Recipe) 
    @recipe.should_receive(:title).and_return(“Grandma’s Soup”) 
    @recipe.should_receive(:servings).and_return(“2”) 
    @recipe.should_receive(:ingredient_string).and_return(“carrots”) 
    @recipe.should_receive(:description).and_return(“description”) 
    @recipe.should_receive(:directions).and_return(“directions”) 
    @recipe.should_receive(:tag_list).and_return(“yummy”) 
    @user = mock_model(User) 
    assigns[:recipe] = @recipe
    assigns[:user] = @user
  end  
  ... 
end

require File.dirname(__FILE__) + ‘/../../spec_helper’
      
describe ‘recipe/new’ do
      
  before(:each) do
    @recipe = mock_model(Recipe)
    @recipe.should_receive(:title).and_return(“Grandma’s Soup”)
    @recipe.should_receive(:servings).and_return(“2”)
    @recipe.should_receive(:ingredient_string).and_return(“carrots”)
    @recipe.should_receive(:description).and_return(“description”)
    @recipe.should_receive(:directions).and_return(“directions”)
    @recipe.should_receive(:tag_list).and_return(“yummy”)
    @user = mock_model(User)
    assigns[:recipe] = @recipe
    assigns[:user] = @user
  end
  ...
end
现 在你对这个模式已经很熟悉了。首先,你创建了一个模实例,并且为它指定了参数。然后将模对象直接加入到assigns 的Hash中。与Controller测试不同,这里不需要模拟ActiveRecord类。通常View也不像Controller那样创建对象,所以 在View中显示对象就足够了。View的测试中也像Controller测试一样用到了assigns, flash和session。



下面的例子演示了没有CAPTCHA对象的行为:



Ruby代码
it “should display an entire form” do
  render “/recipes/new” 
  response.should have_tag(“form”) do
    with_tag “input[name *= title]” 
    with_tag “input[name *= servings]” 
    with_tag “textarea[name *= ingredient_string]” 
    with_tag “textarea[name *= description]” 
    with_tag “textarea[name *= directions]” 
    with_tag “input[name *= tag_list]” 
  end
end 

  it “should display an entire form” do
    render “/recipes/new”
    response.should have_tag(“form”) do
      with_tag “input[name *= title]”
      with_tag “input[name *= servings]”
      with_tag “textarea[name *= ingredient_string]”
      with_tag “textarea[name *= description]”
      with_tag “textarea[name *= directions]”
      with_tag “input[name *= tag_list]”
    end
  end 
想你从代码中看到的那样,render函数为测试模拟执行了指定View。should have_tag和with_tag只是你的老朋友assert_select的同义词。用同样的语法来验证输出里的各种各样HTML元素是否存在。



此外,有一个模板对象,你可以用来模拟或者桩调用Helper函数。因为你可以单独地测试Helper函数。这是一个好主意,使用的是通常的RSpec模框架的语法,看上去像这样:



Ruby代码
template.stub!(:helper_method).and_return(“flintstone”) 
template.should_receive(:helper_method).once.and_return(“rubble”) 

template.stub!(:helper_method).and_return(“flintstone”)
template.should_receive(:helper_method).once.and_return(“rubble”) 
这个对测试登录/登出特别有用,因为在我们的应用程序里这部本代码在Helper函数里。



当然,你也可以模拟别的测试中的其他对象。你可以像这样通过创建一个模对象来验证当存在CAPTCHA对象时View的行为:



Ruby代码
it “should display captcha” do
  @token = mock_model(Token) 
  @token.should_receive(:token).and_return(“a_token”) 
  captcha = mock(MathCaptcha) 
  captcha.should_receive(:display_string).and_return(“display string”) 
  captcha.should_receive(:token).and_return(@token) 
  assigns[:captcha] = captcha 
  render “/recipes/new” 
  response.should have_tag(“form”) do
    with_tag “input[name *= captcha_value]” 
    with_tag “input[name *= token]” 
  end
end 

  it “should display captcha” do
    @token = mock_model(Token)
    @token.should_receive(:token).and_return(“a_token”)
    captcha = mock(MathCaptcha)
    captcha.should_receive(:display_string).and_return(“display string”)
    captcha.should_receive(:token).and_return(@token)
    assigns[:captcha] = captcha
    render “/recipes/new”
    response.should have_tag(“form”) do
      with_tag “input[name *= captcha_value]”
      with_tag “input[name *= token]”
    end
  end 
这 个例子和Controller的测试一样地创建了一个模对象,除了在这个用例中在CAPTCHA中放置了另外的模变量(也就是View将会请求显示字符串 和Token)。CAPTCHA放在assigns的Hash中,这样View就可以渲染它了。(重要的安全提示:在启动渲染之前为所有变量赋值,否则测 试将无法发现它们。)这个例子中,为了简洁我只测试了新增的部分。在实际的测试中,你可能希望同时测试一下原来的东西是不是还在。



测试Helper




RSpec允许从View中脱离出来单独测试Helper函数。Helper测试文件放在spec/helpers目录下,文件名和描述对象都需要与被测试的Helper想对应。例如,在application_helper.rb文件中有这样的函数:



Ruby代码
def inflect(singular, count, plural = nil) 
  plural ||= singular.pluralize 
  if count == 1 then singular else plural end
end 

  def inflect(singular, count, plural = nil)
    plural ||= singular.pluralize
    if count == 1 then singular else plural end
  end 
在spec/helpers/application_helper_spec.rb中应该有这样的测试:



Ruby代码
require File.dirname(__FILE__) + ‘/../spec_helper’ 
         
describe ApplicationHelper do
         
  it “should inflect a word” do
    inflect(“banana”, 3).should == “bananas” 
    inflect(“banana”, 1).should == “banana” 
    inflect(“is”, 2, “are”).should == “are” 
  end
end 

require File.dirname(__FILE__) + ‘/../spec_helper’
      
describe ApplicationHelper do
      
  it “should inflect a word” do
    inflect(“banana”, 3).should == “bananas”
    inflect(“banana”, 1).should == “banana”
    inflect(“is”, 2, “are”).should == “are”
  end
end 
把要测试的Helper类作为describe的参数,所有这个Helper类里面的函数的测试放在里面的行为中。这意味着,你不需要任何引用就可以直接使用这个Helper函数,就像例子里调用inflect函数这样。



没 有什么办法在Helper测试中得到模板或者Controller对象,这也许会出现些难题,比如当你想测试一段直接通过concat将纯文本直接连接到 模板中的代码时。我建议你将文本生成函数与文本插入函数分开,这样可以方便测试,或者在View测试中测试这些Helper。



一些现有的Test::Unit pluging与RSpec有冲突,所以你需要在运行他们之前将你的spec目录删除。
分享到:
评论

相关推荐

    Ruby-EmojiRSpec用于RSpec的自定义Emoji格式化程序

    Ruby-EmojiRSpec是一款专为RSpec设计的自定义格式化程序,它将测试结果以表情符号(Emoji)的形式展示,使得测试输出更加直观和有趣。在Ruby开发过程中,测试是保证代码质量的重要环节,RSpec作为一款强大的行为驱动...

    Ruby-rspecapidocumentation从RSpec自动生成API文档

    RSpec是一个广泛使用的BDD(行为驱动开发)框架,通过编写可读性强的断言来验证代码的行为。rspec_api_documentation则将RSpec的测试用例转化为结构化的API文档,使得开发者可以更直观地了解API接口。 rspec_api_...

    the rspec book.pdf

    《RSpec Book》是一本专注于Rspec的权威指南,它详细阐述了如何使用Rspec这个强大的测试框架进行行为驱动开发(BDD)。Rspec是Ruby编程语言中的一个测试库,它使得编写可读性强、表达力丰富的测试代码成为可能。这...

    行为驱动设计: Professional Ruby on Rails - Chapter 7

    行为驱动设计(Behavior Driven Development,BDD)是一种软件开发方法,它强调通过明确的、以用户为中心的描述来引导开发工作,使团队能够更好地理解需求,并确保代码与预期的行为一致。在Ruby on Rails框架中,BDD...

    rspec测试.pdf

    RSpec是一种用于Ruby语言的单元测试框架,它提倡一种称为“行为驱动开发”(Behavior Driven Development,BDD)的测试方式。RSpec允许开发者以自然语言的方式描述软件的行为,使得测试代码更易于理解和维护。 ### ...

    Ruby-RSpec和Minitest匹配器来预防N1查询问题

    RSpec是Ruby中广泛使用的BDD(行为驱动开发)框架,它允许开发者以自然语言的方式编写测试。匹配器是RSpec的核心部分,它们定义了期望的行为。在处理N+1查询问题时,可以使用像`have_n_queries`这样的匹配器,它能...

    tdd-em-ruby-codigo-rspec:我的书“TDD em Ruby”的代码示例(http

    "rspec"是Ruby社区广泛使用的测试框架,它提供了简洁的语法和丰富的匹配器来编写行为驱动的测试。 描述中提到,本书的代码示例是为Ruby 1.9及更高版本设计的,这意味着它们利用了这个版本引入的一些新特性,如改进...

    rspec 入门者学习

    RSpec 是一个流行的 Ruby 测试框架,主要用于行为驱动开发 (Behavior-Driven Development, BDD)。RSpec 的设计目的是让测试更加自然和易读,使得开发人员能够更好地理解和编写测试用例。 #### 二、RSpec 与 BDD **...

    rspec:Ruby 的行为驱动开发框架

    **rspec: Ruby的行为驱动开发框架** RSpec 是一个广受欢迎的 Ruby 语言的测试框架,专门用于实现行为驱动开发(Behavior-Driven Development, BDD)。它通过提供清晰、易读的语法来定义预期的行为,使得代码更加...

    The.RSpec.Book

    - **RSpec**(RSpec)是一种为Ruby编程语言设计的行为驱动开发(Behavior Driven Development, BDD)框架。它通过提供一种简单而强大的语法来描述对象的行为,使得测试代码更加清晰易读。 - **行为驱动开发**(BDD)...

    jruby-1.5.5+OperaWatir+RSpec

    RSpec 是一个流行的 Ruby 测试框架,主要用于行为驱动开发(BDD)。它提供了一种声明式的方式来描述对象应该如何表现,以及期望的行为结果。RSpec 的语法使得测试代码更接近自然语言,让非程序员也能理解测试目的。...

    Pragmatic.The RSpec Book.2010.pdf

    **RSpec** 是 Ruby 社区中最受欢迎的行为驱动开发(Behavior Driven Development, BDD)框架之一。它为开发者提供了一种灵活的方式来定义应用程序的行为,并通过简洁易读的语法来编写测试用例。 **特性:** 1. **...

    The Rspec Book 最新版 (带Cucmber章节) - 最好的BDD书籍

    《RSpec Book》是关于行为驱动开发(BDD)的一本权威书籍,特别是针对Rspec这一Ruby语言的测试框架。本书的最新版包含了Cucumber章节,使得读者能够更好地理解和实践BDD理念。 行为驱动开发(BDD)是一种软件开发...

    RSpec 3.1中文版

    RSpec是Ruby语言开发的一款行为驱动开发(BDD)工具,它通过使用领域特定语言(DSL)来帮助开发人员编写测试用例。RSpec 3.1版本是RSpec框架的更新版,提供了更多的功能和更好的用户体验。Rails是一个用Ruby语言编写的...

    RSpec-bank-main.rar

    这个项目的源码深入揭示了如何使用RSpec进行行为驱动开发(BDD)以及如何构建一个简单但完整的银行应用。下面,我们将详细探讨其中的关键知识点。 一、RSpec介绍 RSpec是Ruby社区广泛使用的测试框架,它以自然语言...

    BDD开发之rspec和cucumber

    行为驱动开发(Behavior-Driven Development, BDD)是一种软件开发方法论,它结合了敏捷开发的思想和技术,如测试驱动开发(TDD)和领域驱动设计(DDD),以确保软件产品的功能符合最终用户的需求。BDD的主要目标是...

    使用RSpec 测试Rails 程序.pdf

    **RSpec**(RSpec is not a unit testing framework)是一种为Ruby编程语言设计的行为驱动开发(BDD)框架,而**Rails**是基于Ruby的一个全栈web应用框架。将RSpec与Rails相结合可以有效地进行单元测试、集成测试...

    The Rspec Book -- BDD methodology

    **RSpec**是一种广泛应用于Ruby生态系统的测试框架,它支持一种称为**行为驱动开发(BDD)**的方法论。BDD不仅是一种测试方法,更是一种软件开发的哲学,强调的是将开发过程中的关注点放在行为上,而不仅仅是功能实现...

Global site tag (gtag.js) - Google Analytics