首页 > 解决方案 > RSpec:迭代器中的存根方法

问题描述

我想学习如何使用存根。

class SomeClass

attr_reader :current_user

  def initialize(current_user:)
    @current_user = current_user
  end

  def deliver
    subscribers.each do |user|
      DailyEmail.new(recipient: user).deliver
    end

    201
  end

  private

  def subscribers
    User.all.select(&:email_notifications_enabled?)
  end
end

什么是测试 DailyEmail 新的正确方法,从 SomeClass 调用的传递方法。如果订阅者是活动记录关系,我如何测试每种方法?我如何检查迭代器后的状态返回?

我奇怪的解决方案:

RSpec.describe SomeClass do

  let(:current_user) { 'user' }
  subject { described_class.new(current_user: current_user) }

  describe '#deliver' do
    let(:subscribers) { ['test2', 'test1'] }

    context 'when `each`, `new`, `deliver` methods called in controller `deliver` method' do
      it 'calls methods' do
        allow(subscribers).to receive(:each)

        subscribers.each do |user|
          the_double = instance_double(DailyEmail)
          expect(DailyEmail).to receive(:new).and_return(the_double).with(recipient: user)
          expect(the_double).to receive(:deliver)
          expect(subscribers).to have_received(:each)
          subject.deliver
        end
      end
    end
  end
end

我写了一些东西,但这个实现对我来说似乎很糟糕。我不明白如何处理迭代器以及如何测试状态。请给一些提示

标签: ruby-on-railsrubyrspecstub

解决方案


这里有几件事。

首先是您的控制器状态消息是错误的。您现在所做的是返回 201 正文,而不是状态。它应该返回 200,您正在执行一个发送电子邮件的操作,而不是创建对象。如果您不想返回除了肯定的错误消息之外的任何其他内容并且不处理错误消息(您应该这样做),您应该替换201为:render status: 200

您的测试目前并没有真正测试任何东西。如果您的订阅者方法有错误,它不会捕获它,如果您的邮件类有一个错误,您将不会捕获它,那么重点是什么。

对于控制器本身的逻辑,您应该正确循环通过要传递的邮件,并检查它们是否已传递https://relishapp.com/rspec/rspec-rails/docs/mailer-specs或拆分测试在两个。您使用控制器测试测试快乐路径并为电子邮件类创建另一个测试。

运行控制器测试的正确方法是使用请求规范,并期望正确的响应代码。请注意,如果您在测试数据库中启用了订阅者,它将返回 200,如果没有订阅者,它也只会返回 200。整个代码仍在测试中。如果订阅方法中有错误,它将返回 500 错误。https://relishapp.com/rspec/rspec-rails/docs/request-specs/request-spec

要正确测试控制器测试,您需要在测试数据库中创建对象,然后循环遍历它们,而不是尝试像那样模拟它。例如,您可以使用 FactoryBot 执行此操作,或者您甚至可以删除用户模型,如果您由于某种原因无法像这样添加 FactoryBot;取决于您在邮件类中所做的事情。

 before :each do 
   stub_const('User', MockedUserModel)
 end
 
 class MockedUserModel < User
   def all
     arr_of_mocked_users = []
     arr_of_mocked_users << User.new(name: 'mocked_user_1', id: 1)
     arr_of_mocked_users << User.new(name: 'mocked_user_1', id: 2)
     arr_of_mocked_users 
   end

   def email_notifications_enabled?
     true
   end
 end

推荐阅读