如何调整请求测试以验证notify_referrer方法被调用?
如何验证notify_referrer方法被调用(避开allow_any_instance_of和assigns)
问题场景
控制器create方法逻辑如下:
def create @referral = Referral.new(referral_params) if @referral.save @referral.notify_referrer @referral.create_referral_created_event render json: @referral, status: :created else render json: @referral.errors.full_messages, status: :unprocessable_entity end end
需要编写请求测试验证notify_referrer方法被调用,但测试运行前无法访问assigns(:referral),且不想使用allow_any_instance_of,现有测试片段如下:
it 'notifies the referrer' do allow(_____).to receive(:notify_referrer) post api_referrals_path, params: { referral: { referrer_id: referrer.id, referred_id: referred.id } }, headers: { HTTP_AUTHENTICATION: token } expect(assigns(:referral)).to have_received(:notify_referrer) end
可行解决方案
方案1:使用实例替身(Instance Double)模拟控制器逻辑
通过创建Referral的实例替身,拦截控制器中Referral.new的调用,直接验证替身的方法调用情况,完全不需要依赖数据库或assigns:
it 'notifies the referrer' do # 创建Referral实例替身 referral_double = instance_double(Referral) # 让Referral.new返回这个替身,用hash_including匹配参数(避免硬编码完整params) allow(Referral).to receive(:new).with( hash_including(referrer_id: referrer.id, referred_id: referred.id) ).and_return(referral_double) # 模拟save成功的场景 allow(referral_double).to receive(:save).and_return(true) # 存根需要验证的方法 allow(referral_double).to receive(:notify_referrer) allow(referral_double).to receive(:create_referral_created_event) # 发送请求 post api_referrals_path, params: { referral: { referrer_id: referrer.id, referred_id: referred.id } }, headers: { HTTP_AUTHENTICATION: token } # 直接验证替身是否收到了notify_referrer调用 expect(referral_double).to have_received(:notify_referrer) end
方案2:请求后查询数据库实例并验证
如果希望测试真实的数据库写入逻辑,而非完全模拟,可以先存根notify_referrer方法,请求后从数据库中获取实例再验证:
it 'notifies the referrer' do # 存根Referral实例的notify_referrer方法,保留原逻辑同时记录调用 allow_any_instance_of(Referral).to receive(:notify_referrer).and_call_original # 发送请求 post api_referrals_path, params: { referral: { referrer_id: referrer.id, referred_id: referred.id } }, headers: { HTTP_AUTHENTICATION: token } # 从数据库中获取刚创建的Referral实例 referral = Referral.find_by(referrer_id: referrer.id, referred_id: referred.id) # 验证该实例是否调用了notify_referrer expect(referral).to have_received(:notify_referrer) end
方案3:使用Spy实例替代Double
如果觉得实例替身的配置太繁琐,可以用instance_spy简化,它会自动存根所有方法并记录调用:
it 'notifies the referrer' do # 创建Spy实例,自动存根所有方法并记录调用 referral_spy = instance_spy(Referral) allow(Referral).to receive(:new).with( hash_including(referrer_id: referrer.id, referred_id: referred.id) ).and_return(referral_spy) allow(referral_spy).to receive(:save).and_return(true) post api_referrals_path, params: { referral: { referrer_id: referrer.id, referred_id: referred.id } }, headers: { HTTP_AUTHENTICATION: token } # 直接验证spy的方法调用 expect(referral_spy).to have_received(:notify_referrer) expect(referral_spy).to have_received(:create_referral_created_event) end
内容的提问来源于stack exchange,提问作者Jeremy Thomas
相关产品推荐
相关产品推荐

