ruby-on-rails 组织和/或存储我在RSpec中一直使用的模拟对象的最佳方法是什么?

1bqhqjot  于 2023-02-17  发布在  Ruby
关注(0)|答案(1)|浏览(104)

我构建了一个客户端类,它向Discord API发送请求。我模拟了这个客户端,如下面的示例代码所示。请参见方法#mock_client:

require 'rails_helper'
require 'discord_client'

RSpec.describe some_service_class do
  describe '#call' do
    let(:client) { mock_client }

    it 'does this using discord_client' do
      client
      
      described_class.new.call
      
      expect(client).to have_received(:new).once
      expect(client).to have_received(:get_guild).once
    end
  end

  private

  def mock_client
    client = instance_double(DiscordClient)

    allow(DiscordClient).to receive(:new).and_return(client)
    allow(client).to receive(:get_guild)
    allow(client).to receive(:get_user)

    client
  end
end

然而,由于我在许多服务和rake任务中使用这个客户端,我不想总是在我编写的每个spec文件中模拟和存根它。我可以把我的方法#mock_client移到哪里,以便我可以在任何spec文件中调用它?

mwkjh3gx

mwkjh3gx1#

在RSpec中,你可以使用shared contexts来共享你的测试依赖项(let,let!)或者测试设置。这基本上是一个在示例组的上下文中评估的块,它包含在:

RSpec.shared_context "Discord mocks" do
  let(:client) { instance_double(DiscordClient) }

  before do
    allow(DiscordClient).to receive(:new).and_return(client)
    allow(client).to receive(:get_guild)
    allow(client).to receive(:get_user) 
  end
end

这些可以手动包含在include_context的单个规范中,也可以通过规范设置包含。共享上下文通常位于/spec/support中的某个位置。
另外,如果不需要单独的初始化和“call”参数,那么首先可以通过提供工厂方法来减少存根的需要,而不是使用new.get_guild

class DiscordClient
  def self.get_guild(...)
    new.get_guild(...)
  end
end

然后,您需要做的就是对客户端公开的类方法进行存根处理:

allow(DiscordClient).to receive(:get_guild)

您会发现这种模式在服务对象中广泛使用。

相关问题