Rspec的instance_double创建间歇性规范失败

时间:2014-09-10 03:25:49

标签: ruby-on-rails ruby rspec tdd rspec-rails

使用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

它是超级香草。

当我运行rakerspecbin/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%的时间通过。

1 个答案:

答案 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