RSpec测试报错:期望方法调用1次却收到0次,如何针对特定User实例断言?
问题
测试用例返回如下错误:
expected: 1 time with any arguments
received: 0 times with any arguments
明明对应的Active Record确实调用了目标方法,但测试始终不通过。相关代码如下:
测试代码
context "when the City has updated" do let!(:city) { create(:city, ...) } let!(:user) { create(:user, city: city) } let!(:user2) { create(:user, city: city) } it "should call the process_schedule_period for each of the user" do expect(user).to receive(:create_new_period_for_mayor) expect(user2).to receive(:create_new_period_for_mayor) city.update_attribute(:mayor, "mr alford") Delayed::Worker.new.run Delayed::Job.find_by(process_identifier: "process_city_changes") end end
create_new_period_for_mayor方法在延迟任务中被调用,我已经手动运行了该延迟任务。
模型代码(city.rb)
after_save回调
self.delay(process_identifier: "process_city_changes").process_city_data_changes
对应方法实现
def process_city_data_changes users = self.users users.each do |user| user.create_new_period_for_mayor end end
我已经通过调试器确认每个目标用户实例的create_new_period_for_mayor方法都被调用了,但测试始终返回上述错误。用expect_any_instance_of(User)时测试能通过,但我需要针对每个特定的user实例做断言,而且确认调用方法的就是我在测试里创建的那两个实例,请问问题出在哪?
解决方案
问题出在延迟任务序列化实例时,会重新从数据库加载对象,导致测试里的user/user2实例和延迟任务中调用方法的实例不是同一个内存对象。
RSpec的expect(obj).to receive(:method)是针对特定内存实例做的断言,而延迟任务运行时,self.users会从数据库重新查询出User记录,这些是新的内存实例,和测试代码里提前创建的user/user2不是同一个对象,所以断言不会命中。
解决办法有两种:
- 方法一:在延迟任务执行前,把测试用的实例重新从数据库加载,更新断言的目标对象
it "should call the process_schedule_period for each of the user" do city.update_attribute(:mayor, "mr alford") # 重新加载实例,确保和延迟任务里的实例一致 user.reload user2.reload expect(user).to receive(:create_new_period_for_mayor) expect(user2).to receive(:create_new_period_for_mayor) Delayed::Worker.new.run Delayed::Job.find_by(process_identifier: "process_city_changes") end - 方法二:用实例的唯一标识(比如id)来验证调用,结合
and_call_original确保方法正常执行,同时验证调用的是目标实例it "should call the process_schedule_period for each of the user" do expect_any_instance_of(User).to receive(:create_new_period_for_mayor) do |instance| expect([user.id, user2.id]).to include(instance.id) end.and_call_original city.update_attribute(:mayor, "mr alford") Delayed::Worker.new.run Delayed::Job.find_by(process_identifier: "process_city_changes") end
内容的提问来源于stack exchange,提问作者jansha
相关产品推荐
相关产品推荐

