ActiveRecord:模拟 has_many 关系调用
ActiveRecord: Mock has_many relation call
我对测试还很陌生,所以我一直在努力使用正确的语法,尤其是关于 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)
- 第三种是为您拥有的汽车实际设置预订...
这些选项之间的区别是在与代码内部紧密纠缠(即更难维护)、速度 运行 或实际测试代码的所有方面之间进行权衡。
第三个确保一切正常(你有一辆真实预订的真实汽车),但速度较慢,因为它在数据库中设置实际模型......这就是你想要得到的东西过去。
第一个和第二个由你决定。我个人有一种 "ick" 感觉当你测试一个控制器时,发现汽车是你希望测试的一部分......
另外 - 它只会找到您之前设置的汽车,因此您不妨在任何实例上做一个存根。
所以:
expect_any_instance_of(Car).to receive(:bookings).and_return([ Booking.new ])`
可能会成功。
Rspec any_instance_of doco
我对测试还很陌生,所以我一直在努力使用正确的语法,尤其是关于 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)
- 第三种是为您拥有的汽车实际设置预订...
这些选项之间的区别是在与代码内部紧密纠缠(即更难维护)、速度 运行 或实际测试代码的所有方面之间进行权衡。
第三个确保一切正常(你有一辆真实预订的真实汽车),但速度较慢,因为它在数据库中设置实际模型......这就是你想要得到的东西过去。
第一个和第二个由你决定。我个人有一种 "ick" 感觉当你测试一个控制器时,发现汽车是你希望测试的一部分......
另外 - 它只会找到您之前设置的汽车,因此您不妨在任何实例上做一个存根。
所以:
expect_any_instance_of(Car).to receive(:bookings).and_return([ Booking.new ])`
可能会成功。
Rspec any_instance_of doco