我是测试的新手,所以我一直在努力使用正确的语法,尤其是关于mocks
。
我想在 cars_controller.rb
中测试我的destroy
操作 def destroy
if current_user.cars.exists?(params[:id])
car = current_user.cars.find(params[:id])
# only destroy the car if it has no bookings
car.destroy unless car.bookings.exists?
end
redirect_to user_cars_path(current_user)
end
当没有与汽车相关的预订时,测试情况很容易。
describe CarsController, type: :controller do
let(:user) { create(:user_with_car) }
before { login_user(user) }
describe "DELETE #destroy" do
let(:car) { user.cars.first }
context "when the car has no bookings associated to it" do
it "destroys the requested car" do
expect {
delete :destroy, user_id: user.id, id: car.id
}.to change(user.cars, :count).by(-1)
end
end
但是这项测试使我发疯:
context "when the car has bookings associated to it" do
it "does not destroy the requested car" do
##### This line fails miserably
allow(car).to receive_message_chain(:bookings) { [ Booking.new ]}
expect {
delete :destroy, user_id: user.id, id: car.id
}.to change(user.cars, :count).by(0)
end
end
end
end
我不想在数据库中创建预订并将其与汽车相关联。据我所知,建议嘲笑这些预订,因为它们没有其他用途。
旁边:
allow(car).to receive_message_chain(:bookings) { [ Booking.new ]}
我已经与其他语法进行了多次尝试,但都失败了。我什至尝试使用rpsec mocks旧语法: stub(...)
。
我将如何完成此操作?
此原因不起作用,是因为删除操作加载了自己的car
版本 - 它不使用您在本地声明的本地变量。因此,您添加到本地变量中的任何存根实际上都不存在于控制器操作中的car
的全新副本上。
有几种方式。
- 一个是将您的控制器拿出的汽车存根。
- 另一个是简短
any_instance_of(Car)
- 第三个是实际设置您拥有的汽车的预订...
这些选项之间的区别是与代码内部纠缠(即更难维护),运行速度或实际测试代码的所有方面之间的折衷。
第三个要确保一切都真正起作用(您有一辆真正的预订汽车),但是它较慢,因为它在DB中设置了实际模型...这就是您要过去的固执。
第一和第二取决于您。我个人对测试控制器进行测试时有一种" iCk"的感觉,其中找到汽车是您希望正在测试的一部分...
plus-只能找到您之前设置的汽车,因此您不妨在任何情况下进行存根。
so:
expect_any_instance_of(Car).to receive(:bookings).and_return([ Booking.new ])`
可能会解决这个问题。
rspec any_instance_of doco