在我开始嘲笑之前,规范的组织方式对我来说非常清楚。这是一个控制器的例子和它的销毁行动的规范:
控制器:
class UsersController < ApplicationController
def destroy
@user = User.find(params[:id])
if @user.destroy
flash[:success] = "user deleted"
redirect_to users_url
else
flash[:error] = "user could not be deleted"
redirect_to users_url
end
end
end
规格:
require 'spec_helper'
describe UsersController do
let(:user){ FactoryGirl.build(:user) }
describe '#destroy' do
before do
delete :destroy
end
it { flash[:success].should eq "user deleted" }
end
end
但是,如果我想删除destroy方法,我必须这样做:
require 'spec_helper'
describe UsersController do
let(:user){ FactoryGirl.build(:user) }
it "should assign user and populate the error flash" do
User.should_receive(:find).and_return user
User.any_instance.should_receive(:destroy).and_return false
delete :destroy, id: user.id
assigns(:user).should eq user
flash[:error].should == "user could not be deleted"
end
it "should assign user and populate the success flash" do
User.should_receive(:find).and_return user
User.any_instance.should_receive(:destroy).and_return true
delete :destroy, id: user.id
assigns(:user).should eq user
flash[:success].should == "user deleted"
end
end
首先,我可以说这对我来说真的很奇怪。我完全看到了模拟的好处,它如何使它更快,更不依赖于User#destroy
方法的有效性,但我发现你在触发方法的代码之前编写模拟真的很奇怪。有人可以解释一下吗?您是否想以一种先发制人的方式编写模拟,为期望做好准备?
我应该如何编写上面的模拟以保持干燥?我应该使用这样的上下文(未经测试):
require 'spec_helper'
describe UsersController do
shared_examples_for 'it finds the user' do
it { assigns(:user).should eq user }
end
describe '#destroy' do
context 'finds user' do
before { User.should_receive(:find).and_return user }
context 'deletes successfully' do
before do
User.any_instance.should_receive(:destroy).and_return true
delete :destroy, id: user.id
end
it_should_behave_like 'it finds the user'
it{ flash[:success].should eq 'user deleted' }
it{ flash[:error].should be_nil }
end
context 'deletes unsuccessfully' do
before do
User.any_instance.should_receive(:destroy).and_return false
delete :destroy, id: user.id
end
it_should_behave_like 'it finds the user'
it{ flash[:success].should be_nil }
it{ flash[:error].should eq 'user could not be deleted' }
end
end
end
end
它非常干,但我不喜欢delete :destroy, id: user.id
的重复,而且它也不容易阅读。
答案 0 :(得分:0)
而不是
User.should_receive(:find).and_return user
User.any_instance.should_receive(:destroy).and_return false
你为什么不这样做
User.stub!(find: user)
user.stub!(destroy: false)
在前一个块上添加期望是非常好的做法,只需将它们存根
另外,当你存根用户#发现你不需要给一个真正的id来销毁时,你可以像这样调用destroy
delete :destroy
id无关紧要
你应该重新考虑测试的名称,检查一下
describe '#destroy' do
context 'finds user' do
before { User.should_receive(:find).and_return user }
context 'deletes successfully' do
输出将是
UsersController#destroy finds user deletes successfully
它听起来不对,你也没有测试那个,你正在测试,如果用户被成功删除,那么flash [:success]等于某些东西而flash [:error]等于另一个东西
我会这样重构:
require 'spec_helper'
describe UsersController do
let(:user){ FactoryGirl.build(:user) }
describe '#destroy' do
context 'with a valid user id' do
it 'finds the user' do
User.should_receive(:find).once.with(user.id).and_return(user)
delete :destroy, user_id: user.id
assigns(:user).should eq user
end
context 'when the user is found' do
before { User.stub!(find: user) }
context 'and the user can be destroyed' do
before do
user.stub!(destroy: true)
delete :destroy
end
it{ flash[:success].should eq 'user deleted' }
it{ flash[:error].should be_nil }
end
context 'and the user can not be destroyed' do
before do
user.stub!(destroy: false)
delete :destroy
end
it{ flash[:success].should be_nil }
it{ flash[:error].should eq 'user could not be deleted' }
end
end
end
end
end
现在输出
UsersController#destroy with a valid user_id when the user is found and can be destroyed...
我认为这样阅读更容易,你真的解释了规范的设置
检查我做了一个更改,你真的不需要shared_example来查找用户