中正确模拟内部服务

中正确模拟内部服务

本文介绍了如何在 RSpec 中正确模拟内部服务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想学习如何在其他类中正确模拟对象调用,例如我有这个控制器操作:

I would like to learn how to properly mock object calls inside other classes, foor example I have this controller action:

def show
 service = Action::PartsShow.new(show_params, current_user)
 service.call
 render json: service.part, root: :part, serializer: PartSerializer, include: '**',
        scope: {current_user: current_user}
end

服务类看起来像这样.

module Action
  class PartsShow < PartsShowBase
    def find_part
      ...
    end
  end
end

module Action
  class PartsShowBase
    attr_reader :part

    def initialize(params, current_user)
      @params = params
      @current_user = current_user
    end

    def call
      find_part
      reload_part_availability
      reload_part_price if @current_user.present?
    end

    private

    def reload_part_availability
      ReloadPartAvailabilityWorker.perform_async(part.id)
    end

    def reload_part_price
      ExternalData::LauberApi::UpdatePrices.new(@current_user, [part]).execute
    end
  end
end

我不想在这个控制器操作和所有其他方法中调用实际的 Action::PartsShow 服务,服务 + 工作人员,因为这使得测试非常慢.我想要的是测试是否正在调用此服务并模拟其余服务.我不想在我的测试中调用它们,我想模拟它们.

I don't want to call the actual Action::PartsShow service inside this controller action and all other methods, services + the worker because this makes the test very slow. What I want is to test if this service is being called and mock the rest of the services. I don't want to call them in my tests, I want to mock them.

我的测试如下:

RSpec.describe PartController, type: :request do
  describe 'GET #show' do
    let(:part) { create(:part) }

    subject { get "/api/v1/parts/#{part.id}" }

    expect(response_body).to eq(200)
    # ...
  end
end

你能告诉我如何正确地模拟它吗?我读过 RSpec 模拟和存根,但我对此感到困惑.感谢您的帮助.

Could you show me how to properly mock it? I've read about RSpec mocks and stubs but I am confused about it. I would appreciate your help.

推荐答案

通过 rspec-mocks gem,您可以使用 allow_any_instance_of.通常,这部分位于 before 块中.

With rspec-mocks gem, you can use allow_any_instance_of. Usually, this part lies in before block.

其实Action::PartsShow是负责加载一个part的,所以不需要泄露两个实例方法:callpart.您可以通过从 call 返回部分来简化它.

In fact,Action::PartsShow is responsible for loading a part, so there is no need to leak two instance methods: call and part. You can simplify it through returning the part from call.

module Action
  class PartsShowBase
    #attr_reader :part

    def call
      find_part # assign @part
      reload_part_availability
      reload_part_price if @current_user.present?
      @part
    end
    ...
end
RSpec.describe PartController, type: :request do
  before :all do
    allow_any_instance_of(Action::PartsShow).to receive(:call).and_return(returned_part)
  end

参考

https://relishapp.com/rspec/rspec-mocks/v/3-5/docs/working-with-legacy-code/any-instance

这篇关于如何在 RSpec 中正确模拟内部服务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-20 20:38