我正在测试我的控制器,并且有一个产生AR查询的字符串:
current_user.providers.find(params[:id])
。我需要它来准确返回我测试的对象,否则,控制器获得的参考不同于我在规范中的参考,并且某些存根(如allow(provider).to receive(:recreate)
不起作用。
我发现这样做的唯一方法就是使用receive_message_chain
这样:
allow(provider.user).to receive_message_chain(:providers, :find => provider)
。但是rspec文档says要考虑使用receive_message_chain
作为闻起来的代码。
另外,我想以后我可能需要用另一个id调用current_user.providers.find(otherid)
来获取另一个对象,这样对我来说就不再适用了。
有没有办法做得更好?我已经设法避免allow_any_instance_of
,这也被认为是臭,所以我相信有一种方法可以避免这种情况,我只是看不到它。
如果没有,我至少想知道是否有办法将with
添加到receive_message_chain
?
===========
我只是尝试测试控制器的update
方法。
# app/controllers/restream/facebooks_controller.rb
class Restream::FacebooksController < Restream::BaseController
def update
current_user.providers.find(params[:id])
if @fb.update_attributes(facebook_params)
if event_changed?
@fb.recreate
else
@fb.update
end
redirect_to restreams_path
else
render 'edit'
end
end
end
#spec/controllers/restream/facebooks_controller_spec.rb
require 'rails_helper'
describe Restream::FacebooksController do
let!(:facebook) { create(:restream_facebook) }
let!(:restream) { facebook.restream }
before do
login(restream.user)
end
describe '#update' do
let!(:params_hash) { {
:title => facebook.title,
:privacy => facebook.privacy,
:destination => facebook.destination,
:destination_id => facebook.destination_id,
:description => facebook.description
} }
let!(:request_hash) { {
:restream_facebook => params_hash,
:id => facebook.id
} }
before do
allow(facebook.user).
to receive_message_chain(:providers, :find => facebook)
allow(facebook).to receive(:update)
allow(facebook).to receive(:recreate)
end
context 'updates' do
it 'title' do
params_hash[:title] = SecureRandom.hex(2)
post :update, request_hash
expect(facebook.reload.title).to eq params_hash[:title]
end
end
end
end
答案 0 :(得分:0)
通过从链中删除current_user
,您的控制器是否也能正常工作? Provider.find(params[:id])
这样你就可以使用更少链接的方法和更简单的代码进行测试。我认为current_user.providers.find
链不只是Provider.find
。
答案 1 :(得分:0)
receive_messaged_chain
可能是一种气味,但在测试控制器时使用双倍更大的臭味。
由于您没有提供任何控制器操作代码,我会举几个例子:
def destroy
@provider = current_user.providers.find(params[:id])
@provider.delete
end
不要测试是否调用delete
方法。测试对象是否从DB中消失,例如:
let(:current_user) { FactoryGirl.create(:user) }
let!(:provider) { FactoryGirl.create(:provider, user: current_user }
it do
delete :destroy, id: provider.id
expect(Provider.find(provider.id).to raise_error(ActiveRecord::RecordNotFound) # writing from memory, don't remember exactly how the exception is called
end
# or
it do
expect { delete :destroy }
.to change{ Provider.where(id: provider.id).count }.from(1).to(0)
end
等等。通常,您希望使用双打作为最后的解决方案。想一想如何根据实际效果来测试代码,而不是它调用的方法。这适用于任何集成测试(控制器都是)。如果您正在编写单元测试并需要隔离 - 那么就可以通过模拟来实现这种隔离。