16

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

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

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

4

2 回答 2

45

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

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

将导致 rspec 执行以下操作:

  1. 运行User.count并记下返回的值。(这可以指定为接收器和方法名称,就像(User, :count)在您的示例中一样,也可以指定为任意代码块,例如{ User.count }.
  2. Runclick_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)

一些文档在这里

它是如何做到的有点神秘,而且完全没有必要了解它是如何工作的以便使用它。调用expect是定义一个结构供 rspec 执行,使用 rspec 自己的自定义 DSL 和“匹配器”系统。Gary Bernhardt 有一个相当简洁的截屏视频,他认为 rspec 的奥秘实际上是从像 ruby​​ 这样的动态语言中自然而然地消失的。这不是一个很好的使用rspec 的介绍,但如果你对它的工作原理感到好奇,你可能会觉得它很有趣。

更新

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

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

于 2012-05-18T01:55:41.060 回答
4

这是Ryan Bates Railscast 关于 Request Specs 和 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  

这是RSPec 期望文档的摘录

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"

(这里是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 
于 2012-05-18T01:37:14.540 回答