问题描述
我想学习如何在其他类中正确模拟对象调用,例如我有这个控制器操作:
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的,所以不需要泄露两个实例方法:call
和part代码>.您可以通过从
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 中正确模拟内部服务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!