InstanceDouble(session)(匿名)> 收到意外消息:[]= with

InstanceDouble(session) (anonymous)> received unexpected message :[]= with

我对 rspec 行为有疑问。我尝试为我使用 session 的服务编写测试,以读取一些值并覆盖该值。

比如我要测试的东西

class CurrentCartService
  attr_reader :user, :session

  def initialize(user, session)
    @user = user
    @session = session
  end

  def cart_id
    { id: session[:cart_id] }
  end

  def assigne_cart_to_session
    session[:cart_id] = current_cart.id
  end

spec

describe CurrentCartService do
  let(:current_user) { user }
  let(:session) { double('session') }

  let!(:cart) { create(:cart) }

  subject { described_class.new current_user, session }

  before do
    allow(session).to receive(:[]).and_return(cart.id)
  end

  describe '#call' do
    context 'when user is not signed' do
      let(:user) { nil }
      it { subject.call }
    end
  end
end

binding.pry

session[:cart_id]
=> 574
session[:cart_id] = 123
RSpec::Mocks::MockExpectationError: #<InstanceDouble(session) (anonymous)> received unexpected message :[]= with (:cart_id, 123)

如何解决这个问题?我试着写了一些 expect

expect(session).to receive(:[])

但是还是不行,还是一样的错误

该消息告诉您,您的替身收到消息 []=,参数为 :cart_id123,但没有预料到。

让我们应用一些基本逻辑。问题是:

  1. 收到消息
  2. 这出乎意料

因此,您可以通过两种方式删除该消息:

  1. 不发送消息或
  2. 告诉分身期待消息

第一个很简单:只需删除第 14 行 session[:cart_id] = current_cart.id,因为这是您调用 []= 的唯一地方。但是,我认为这不是你想要做的。

第二条也很简单。有一种叫做 expect 的方法可以让你告诉替身等待某个消息。所以,我们需要做的就是建立一个期望。

好的失败消息在测试框架中非常重要,值得庆幸的是,RSpec确实有好的失败消息。失败消息应该告诉您如何继续进行测试,您引用的消息包含我们需要的所有信息:

  • 引发失败的替身名称 (session)
  • 导致失败的意外消息的名称 ([]=)
  • 甚至传递给消息的参数(:cart_id123

我们所要做的就是,想都不想,直接复制粘贴这些信息:

expect(session).to receive(:[]=).with(:cart_id, 123)

或者,如果我们希望我们的测试不那么脆弱而不是硬编码 123,我们也可以这样做:

expect(session).to receive(:[]=).with(:cart_id, instance_of(Integer))