RSpec如何期望在ROR中发挥作用

时间:2012-05-18 01:25:00

标签: ruby-on-rails ruby rspec capybara

在阅读Michael Hartl撰写的Ruby On Rails教程时,在作者编写集成测试以验证其注册页面的部分中,他使用了代码spinet。我得到了代码所做的但却无法理解“如何”部分,即无法理解执行顺序。

expect { click_button "Create my account" }.not_to change(User, :count)

有人可以解释上述方法和块链的语义以及它们如何组合在一起吗?

2 个答案:

答案 0 :(得分:45)

您使用expect ... change来验证特定方法调用是否更改 - 或不更改 - 其他一些值。在这种情况下:

expect { click_button "Create my account" }.not_to change(User, :count)

将导致rspec执行以下操作:

  1. 运行User.count并记下返回的值。 (这可以指定为接收者和方法名称,例如示例中的(User, :count),或者作为任意代码块,如{ User.count }
  2. 运行click_button "Create my account",这是一种模拟鼠标点击链接的Capybara方法。
  3. 再次运行User.count
  4. 比较#1和#3的结果。如果它们不同,则示例失败。如果它们相同,它会通过。
  5. 其他使用方式expect ... change

    expect { thing.destroy }.to change(Thing, :count).from(1).to(0)
    expect { thing.tax = 5 }.to change { thing.total_price }.by(5)
    expect { thing.save! }.to raise_error
    expect { thing.symbolize_name }.to change { thing.name }.from(String).to(Symbol)
    

    有些文档是here

    它是如何做到这一点有点神秘,而且根本不需要了解它如何使用它。对expect的调用是使用rspec自己定制的DSL和" matchers系统定义rspec执行的结构。"加里·伯恩哈特(Gary Bernhardt)有一个相当整洁的screencast,他认为rspec的奥秘实际上是从像红宝石这样的动态语言中脱离出来的。它不是使用 rspec的好介绍,但如果您对它的工作原理感到好奇,您可能会觉得它很有趣。

    <强>更新

    在看到您对其他答案的评论后,我将添加一些关于操作顺序的内容。不直观的伎俩是它执行所有块的匹配器(在这种情况下为change)。 expect有一个lambda,not_toshould_not的别名,其作用是将lambda传递给匹配器。在这种情况下,匹配器是change,它知道执行一次自己的参数,然后执行传递的lambda(来自expect的那个),然后再次运行自己的参数以查看是否有变化。它很棘手,因为它看起来应该从左到右执行,但由于大多数部分只是传递代码块,所以它们可以并且将它们拖放到对匹配器最有意义的任何顺序中。 / p>

    我不是rspec内部的专家,但这是我对基本理念的理解。

答案 1 :(得分:4)

以下是Ryan Bates Railscast on Request Specs and Capybara

的摘录
require 'spec_helper'  

describe "Tasks" do  
  describe "GET /tasks" do  
    it "displays tasks" do  
      Task.create!(:name => "paint fence")  
      visit tasks_path  
      page.should have_content("paint fence")  
    end  
  end  

  describe "POST /tasks" do  
    it "creates a task" do  
      visit tasks_path  
      fill_in "Name", :with => "mow lawn"  
      click_button "Add"  
      page.should have_content("Successfully added task.")  
      page.should have_content("mow lawn")  
    end  
  end  
end  

以下是the docs on RSPec Expectations

的摘录
describe Counter, "#increment" do
  it "should increment the count" do
    expect{Counter.increment}.to change{Counter.count}.from(0).to(1)
  end

  # deliberate failure
  it "should increment the count by 2" do
    expect{Counter.increment}.to change{Counter.count}.by(2)
  end
end

基本上,

expect { click_button "Create my account" }.not_to change(User, :count)

是RSpec的一部分:

expect {...}.not_to change(User, :count)

和部分Capybara

click_button "Create my account"

(此处为a link to the Capyabara DSL - 您可以搜索click_button

听起来你正在寻找他们两个的整体例子。这不是一个完美的例子,但它可能看起来像这样:

describe "Tasks" do  
  describe "GET /tasks" do  
    it "displays tasks" do  
      expect { click_button "Create my account" }.not_to change(User, :count)
    end  
  end 
end