【问题标题】:How does RSpec's expect work in RORRSpec 的期望如何在 ROR 中工作
【发布时间】:2012-05-18 01:25:00
【问题描述】:

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

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

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

【问题讨论】:

  • 这不是 Capybara,这是 RSpec。
  • @RyanBigg 谢谢,刚刚更正了。

标签: ruby-on-rails ruby rspec capybara


【解决方案1】:

您可以使用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 的结果。如果它们不同,则示例失败。如果它们相同,则通过。

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 执行,使用rspec 自己的自定义DSL 和“匹配器”系统。 Gary Bernhardt 有一个相当简洁的screencast,他认为 rspec 的奥秘实际上是从像 ruby​​ 这样的动态语言中自然而然地消失的。这不是一个很好的使用 rspec 的介绍,但如果你对它的工作原理感到好奇,你可能会觉得它很有趣。

更新

在看到您对另一个答案的评论后,我将添加一些有关操作顺序的信息。不直观的技巧是执行所有块的是 matcher(在这种情况下为change)。 expect 有一个 lambda,not_toshould_not 的别名,其工作是将 lambda 传递给匹配器。在这种情况下,匹配器是 change,它知道执行一次自己的参数,然后执行它传递的 lambda(来自 expect 的那个),然后再次运行自己的参数以查看是否发生了变化。这很棘手,因为该行看起来应该从左到右执行,但由于大多数部分只是传递代码块,因此它们可以并且确实将它们打乱成对匹配器最有意义的任何顺序。

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

【讨论】:

  • 我在 SO 上读过的最详细和最有用的答案。如果可以的话,我会赞成这个 100 倍。谢谢!
【解决方案2】:

这是来自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)

和水豚的一部分

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 

【讨论】:

  • Tnx 是很好的例子。我更感兴趣的是知道表达式的语义,例如剂量 change(..) 如何被执行两次以及执行的总体顺序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多