Rails Rspec测试中的多个期望

时间:2013-06-26 16:12:09

标签: ruby-on-rails rspec

我正在尝试测试一个ruby控制器方法,我期待在数据库中改变多个东西。

context "With an unknown user" do
  let(:unknown_phone_number) { "0000000000" }
  subject {post :create, twiml_message(unknown_phone_number, "YES", "To" => twilio_phone_number) }

  it { response.should change(Card, :count).by(1) }
  it { should change(User, :count).by(1) }
  it { should change(Customer, :count).by(1) }```
end

给出错误

NoMethodError: undefined method `call' for #<ActionController::TestResponse:0x007fbd5d643030> ./spec/controllers/api/v1/sms_controller_spec.rb:25:in `block (4 levels) in <top (required)>'```

我错过了什么,或者我是在咆哮完全错误的树?

版本信息: Rails 1.9.3 Rspec 2.11.0

更新:基于答案

it "registers an unknown user" do
    unknown_phone_number = "0000000000"
    expect {
      post :create, twiml_message(unknown_phone_number, "YES", "To" => twilio_phone_number)
    }.to change(Card, :count).by(1)

    open_last_text_message_for unknown_phone_number
    current_text_message.should have_body "Welcome to OnTab. You are now registered with a test account. To get started text TAB to open a tab."
  end

  it "registers an unknown user" do
    unknown_phone_number = "0000000000"
    expect {
      post :create, twiml_message(unknown_phone_number, "YES", "To" => twilio_phone_number)
    }.to change(Customer, :count).by(1)

    open_last_text_message_for unknown_phone_number
    current_text_message.should have_body "Welcome to OnTab. You are now registered with a test account. To get started text TAB to open a tab."
  end

  it "registers an unknown user" do
    unknown_phone_number = "0000000000"
    expect {
      post :create, twiml_message(unknown_phone_number, "YES", "To" => twilio_phone_number)
    }.to change(User, :count).by(1)

    open_last_text_message_for unknown_phone_number
    current_text_message.should have_body "Welcome to OnTab. You are now registered with a test account. To get started text TAB to open a tab."
  end

但这会使后期动作三次。有没有办法做到这一点,但只执行一次POST?

1 个答案:

答案 0 :(得分:2)

您可能希望将此类测试作为集成或请求规范。控制器应该更多地关于断言传递给模型的消息和设置视图/返回状态。但是,为了回答这个问题,response对象上没有call方法。它是返回的状态 - 事情已经发生了变化。你想断言post做了改变。

expect change匹配器需要附加到expect的块形式:

it do
  expect{ post :create, ... }.to change(User, :count).by(1)
end

您当前的主题不是预期阻止,而是post操作的返回值。因此,隐含主题不适用于change。另外,正如我上面所说,出于同样的原因,你不能使用response.should。您需要使用阻止表格。

对于此类测试,由于RSpec如何处理expect{}.should,您将无法使用隐式主题。

另外,可以说,使主题成为预期阻碍会改变责任和角色。通常,被测对象是一个对象。通过将其设置为动作,您正在改变这一点,并可能会让其他人感到困惑。在这种情况下,这可能看起来很奇怪,但这是由于Rails以及您与控制器对象的交互方式。

基于问题变更的更新:

是的,它会发出三个post个请求。我假设你只想提出一个请求,因为“性能”。这可能是一个问题,但您可以采取许多措施来帮助降低性能,而不是简单地发出一个请求。

加速测试的一个理念是在控制器测试中存根Active Record,以保持简短和快速。然后将集成规范用于您正在讨论的测试类型(阅读http://www.andylindeman.com/2012/11/11/rspec-rails-and-capybara-2.0-what-you-need-to-know.html)。是的,集成测试通常较慢;这是进行全栈测试的代价。

RSpec的理念是“测试一件事”。您希望每次都重新执行测试中的对象/方法。这确保了没有交叉测试污染,这可能导致错误的结果。在编写时,我会说你已经违反了这个测试数据库更新和测试文本消息期望。

context 'registering a new user' do
  it 'saves the user in the database' do
    unknown_phone_number = '000'
    new_card = mock_model(Card)
    expect(Card).to receive(:new).and_return(new_card)

    expect(new_card).to receive(:save).and_return(true)

    post :create #...
  end

  it 'sends a welcome message to the phone' do
    # You can stub the text class here and assert on messages passed
    # As you probably trust that if the message is passed the text message code
    # does what it was designed to do (you have unit tests for it right?)
  end
end