使用instance_double时,我发现间歇性测试失败。
我有一个包含4个规格的文件。这是来源:
require 'rails_helper'
describe SubmitPost do
before(:each) do
@post = instance_double('Post')
allow(@post).to receive(:submitted_at=)
end
context 'on success' do
before(:each) do
allow(@post).to receive(:save).and_return(true)
@result = SubmitPost.call(post: @post)
end
it 'should set the submitted_at date' do
expect(@post).to have_received(:submitted_at=)
end
it 'should call save' do
expect(@post).to have_received(:save)
end
it 'should return success' do
expect(@result.success?).to eq(true)
expect(@result.failure?).to eq(false)
end
end
context 'on failure' do
before(:each) do
allow(@post).to receive(:save).and_return(false)
@result = SubmitPost.call(post: @post)
end
it 'should return failure' do
expect(@result.success?).to eq(false)
expect(@result.failure?).to eq(true)
end
end
end
这是一个Rails 4.1.4应用程序。在内部,SubmitPost设置submitted_at并调用保存在传入的Post上。我的帖子模型看起来像这样:
class Post < ActiveRecord::Base
validates :title, presence: true
validates :summary, presence: true
validates :url, presence: true
validates :submitted_at, presence: true
scope :chronological, -> { order('submitted_at desc') }
end
它是超级香草。
当我运行rake
,rspec
或bin/rspec
时,我得到的所有四项测试都失败了20% - 30%的时间。错误消息始终为:
Failure/Error: allow(@post).to receive(:submitted_at=)
Post does not implement: submitted_at=
如果我使用focus: true
标记其中一个规格,那么一个规格将在100%的时间内失败。
如果我将instance_double
替换为double
,则所有规格都将在100%的时间内成功。
看来,instance_double在推断Post类可用的方法方面有些困难。它似乎也有点随机和基于时间。
有没有人遇到过这个问题?什么想法可能是错的?如何解决这个问题的任何意义?当然,插入调试断点会导致规范100%的时间通过。
答案 0 :(得分:4)
您看到的问题是ActiveRecord动态创建列方法。 instance_double
使用'Post'
查找方法以验证您是否正确对齐它们(除非该类尚未加载或尚未加载)。
当先前规范加载模型时,ActiveRecord将创建这些动态方法,以便您的规范通过,然后RSpec可以找到方法(使用respond_to?
调用)。当单独运行时,模型以前没有被使用过,因此ActiveRecord还没有创建动态方法,并且您的测试会因为您的体验而失败。
解决方法是强制ActiveRecord在规范中调用动态方法时加载它们:
class Post < ActiveRecord::Base
def submitted_at=(value)
super
end
end
有关该问题的进一步说明和解决方法,请参阅RSpec文档:
https://www.relishapp.com/rspec/rspec-mocks/docs/verifying-doubles/dynamic-classes