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

Eri*_*icM 6 ruby tdd rspec ruby-on-rails 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
Run Code Online (Sandbox Code Playgroud)

这是一个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
Run Code Online (Sandbox Code Playgroud)

这是超级香草.

当我跑rake,rspec或者bin/rspec,我得到所有四个测试都失败了20% - 30%的时间.错误消息始终是:

Failure/Error: allow(@post).to receive(:submitted_at=)
  Post does not implement: submitted_at=
Run Code Online (Sandbox Code Playgroud)

如果我标注其中一个规格focus: true,那么一个规格将在100%的时间内失败.

如果我更换instance_doubledouble,所有的规格会成功的100%的时间.

看来instance_double在推断Post类上可用的方法方面有些困难.它似乎也有点随机和基于时间.

有没有人遇到过这个问题?什么想法可能是错的?如何解决这个问题的任何意义?当然,插入调试断点会导致规范100%的时间通过.

Phi*_*ilT 4

您看到的问题是 ActiveRecord 动态创建列方法。instance_double用于'Post'查找方法来验证您是否正确地存根它们(除非该类尚不存在或尚未加载)。

当先前的规范加载模型时,ActiveRecord 将创建这些动态方法,以便您的规范通过,然后 RSpec 可以找到这些方法(通过调用respond_to?)。当单独运行时,该模型以前没有被使用过,因此 ActiveRecord 还不会创建动态方法,并且您的测试会像您所经历的那样失败。

解决方法是强制 ActiveRecord 在规范中调用动态方法时加载它们:

class Post < ActiveRecord::Base
  def submitted_at=(value)
    super
  end
end
Run Code Online (Sandbox Code Playgroud)

请参阅 RSpec 文档以获取该问题的进一步说明和解决方法:

https://www.relishapp.com/rspec/rspec-mocks/docs/verifying-doubles/dynamic-classes