首页 > 解决方案 > Rspec测试调用方法发送再确认指令

问题描述

我正在使用 Rspec 测试用户更改密码时的情况,邮件将被发送。我想检查是否只发送了 1 封邮件。我不想Action::Mailer.deliveries用来检查,而是想检查是否调用了方法以及调用了多少。

在搜索时我Test Spy从 rspec mock 中找到: https ://github.com/rspec/rspec-mocks#test-sies

describe 'PUT /email' do
  include_context 'a user has signed in', { email: 'old-email@example.com', password: 'correct_password' }

  context  'correct new email, correct password' do
    before do
      allow(user).to receive(:send_reconfirmation_instructions)
    end

    it do
      should == 302
      expect(user.reload.unconfirmed_email).to eq 'new-email@example.com'
      expect(user).to receive(:send_reconfirmation_instructions).once
    end
  end
end

但我得到了错误:

  Failure/Error: expect(user).to receive(:send_reconfirmation_instructions)

   (#<User id: 1269, email: “old-email@example.com”, created_at: “2019-08-27 03:54:33", updated_at: “2019-08-27 03:54:33”...“&gt;).send_reconfirmation_instructions(*(any args))
       expected: 1 time with any arguments
       received: 0 times with any arguments

send_reconfirmation_instructions此功能来自设计:https ://github.com/plataformatec/devise/blob/master/lib/devise/models/confirmable.rb#L124-L130

我做到了binding.pry,我确定测试在这个函数内跳转,但 rspec 仍然失败。

编辑:我可以这样写:

describe 'PUT /email' do
  include_context 'a user has signed in', { email: 'old-email@example.com', password: 'correct_password' }

  context  'correct new email, correct password' do
    before do
      expect_any_instance_of(User).to receive(:send_reconfirmation_instructions).once
    end

    it do
      should == 302
      expect(user.reload.unconfirmed_email).to eq 'new-email@example.com'
      # expect(user).to receive(:send_reconfirmation_instructions).once
    end
  end
end

但是我遇到了另一个错误:

Failure/Error: 
(#<User user_id: 1418, email: “old-email@example.com”...“&gt;).send_reconfirmation_instructions(#<User user_id: 1418, email: “old-email@example.com” ...“&gt;)
                expected: 1 time with any arguments
                received: 2 times with arguments: (#<User user_id: 1418, email: “old-email@example.com”, id: 1323...“&gt;)

标签: ruby-on-railsrubyrspecmockingrspec-rails

解决方案


您正在检查用户是否已收到消息的行应为:

expect(user).to have_received(:send_reconfirmation_instructions).once

代替:

expect(user).to receive(:send_reconfirmation_instructions).once

您所说的前者expect(obj).to have_received(:msg)要求您断言该消息已按您的意图被调用。

另一方面,您说后者expect(obj).to receive(:msg)是一种在动作之前设置期望的方法,即代替 theallow(obj).to receive(:msg)而不需要断言它是在动作之后调用的。规范运行后,它会自动断言它是否被调用。

这解释了您在指定时遇到的错误

expect(user).to receive(:send_reconfirmation_instructions).once

因为该行之后没有代码将该消息发送到user,在规范之后得到验证。


推荐阅读