[英]Multiple Expectations in Rails Rspec test
I'm trying to test a ruby controller method, and I'm expecting multiple things to change in the database. 我正在尝试测试一个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
Gives the error 给出错误
NoMethodError: undefined method `call' for #<ActionController::TestResponse:0x007fbd5d643030> ./spec/controllers/api/v1/sms_controller_spec.rb:25:in `block (4 levels) in <top (required)>'```
What am I missing, or am I barking up the completely wrong tree? 我错过了什么,还是我咆哮着完全错误的树?
Version info: Rails 1.9.3 Rspec 2.11.0 版本信息:Rails 1.9.3 Rspec 2.11.0
Update: Based on Answer 更新:基于答案
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
But this runs the post action three times. 但是,这次运行三次。 It there any way to do this, but only execute the POST once?
有没有办法做到这一点,但只执行一次POST?
You probably want this type of test as an integration or request spec. 您可能希望将此类测试作为集成或请求规范。 Controllers should do more about asserting messages passed to models and setup of the view / return state.
控制器应该更多地关于断言传递给模型的消息和设置视图/返回状态。 Though, to answer the question, the
response
object doesn't have a call
method on it. 但是,为了回答这个问题,
response
对象上没有call
方法。 It is the returned state - things have already changed. 它是返回的状态 - 事情已经发生了变化。 You want to assert that the
post
does the changing. 你想断言
post
做了改变。
The expect change
matcher needs to be attached to the block form of expect: expect change
匹配器需要附加到期望的块形式:
it do
expect{ post :create, ... }.to change(User, :count).by(1)
end
Your current subject is not an expectation block, but the return value of the post
action. 您当前的主题不是期望块,而是
post
操作的返回值。 So the implicit subject will not work for change
. 因此隐含的主题不适用于
change
。 Also, as I stated above, you cannot use response.should
for that same reason. 另外,正如我上面所说,出于同样的原因,你不能使用
response.should
。 You need to use the block form. 您需要使用块表单。
For this type of test, you will not be able to use the implicit subject, due to how RSpec handles expect{}.
对于这种类型的测试,由于RSpec处理
expect{}.
,您将无法使用隐式主题expect{}.
versus should
. 与
should
。
Also, arguably, to make the subject an expectation block would change the responsibilities and roles. 此外,可以说,使主题成为预期阻碍会改变责任和角色。 In general the object under test is that, an object.
通常,被测对象是一个对象。 By setting it to an action you're changing this and will likely confuse others down the road.
通过将其设置为动作,您正在改变这一点,并可能会让其他人感到困惑。 This may seem odd in this case, but that is due to Rails and how you interact with controller objects.
在这种情况下,这可能看起来很奇怪,但这是由于Rails以及您如何与控制器对象进行交互。
Yes, it will make three post
requests. 是的,它会发出三个
post
请求。 I'm going to assume you only want to make one request because of "performance". 我假设你只想提出一个请求,因为“性能”。 That can be an issue but there are many things you can do to help mitigate performance instead of simply making one request.
这可能是一个问题,但您可以采取许多措施来帮助降低性能,而不是简单地发出一个请求。
One philosophy for speeding up tests is to stub Active Record in controller tests, that keeps things short and fast. 加速测试的一个理念是在控制器测试中存根Active Record,这样可以保持简短和快速。 Integration specs are then used for the type of testing you are talking about (read http://www.andylindeman.com/2012/11/11/rspec-rails-and-capybara-2.0-what-you-need-to-know.html ).
然后将集成规范用于您正在讨论的测试类型(请阅读http://www.andylindeman.com/2012/11/11/rspec-rails-and-capybara-2.0-what-you-need-to- know.html )。 Yes, integration tests are slower normally;
是的,集成测试通常较慢; that is the price of doing a full stack test.
这是进行全栈测试的代价。
RSpec has a philosophy of 'test one thing'. RSpec的理念是“测试一件事”。 You want to re-execute the object / method under test each time.
您希望每次都重新执行测试中的对象/方法。 This ensures that there is no cross test contamination which can lead to faulty results.
这确保了没有交叉测试污染,这可能导致错误的结果。 As it is written, I would say you are already violating this be testing a database update and testing a text message expectation.
正如它所写,我会说你已经违反了这个测试数据库更新并测试文本消息的期望。
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.