ruby - How to properly mock itnernal services in 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
The service class looks like this.
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
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.
My test looks like this:
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
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.
解决方案
假设find_part
调用Part.find(id)
,您可以添加:
before do
allow(Part).to receive(:find).with(part.id).and_return(part)
end
确保记录查找始终返回您的测试对象。我还建议稍微修改一下您的规范:
RSpec.describe PartController, type: :request do
subject { response }
describe '#show' do
let(:request) { get api_v1_part_path(part.id) }
# If you insist on mocking the object, might as well use build_stubbed
let(:part) { build_stubbed(:part) }
let(:json) { JSON.parse(response.body).deep_symbolize_keys }
let(:expected) {
{
part: {
id: parts.id,
# ...
}
}
}
before do
# This is the recommended way to mock an object
allow(Part).to receive(:find).with(part.id).and_return(part)
request
end
# Validate response status
# https://relishapp.com/rspec/rspec-rails/docs/matchers/have-http-status-matcher
# If you are using shoulda matchers - it works bc subject is the response
it { should have_http_status(:success) }
# otherwise
it { expect(response).to have_http_status(:success) }
# Validate response body
it { expect(json).to eq(expected) }
end
end
如果您的项目有路径助手,我还建议使用它们而不是路径字符串。
推荐阅读
- javascript - 使用 Jquery 引导验证来验证字段数组中的单个字段以及兄弟字段
- gcc - ARM 汇编快速排序和递归
- bash - Bash SED 未终止的“s”命令
- unit-testing - Mockk 方法调用验证:我可以验证是否调用了两种可能的方法之一?
- python - IndentationError: Expected an indented block - Python机器学习猫/狗
- google-cloud-platform - Instance Group 不会使用 GPU 创建实例:没有足够的资源
- sql - 当另一个表被更新时触发更新同一个表中的值
- python - 使用 Elastisearch 中的脚本进行更新
- angular - Angular Material 7 Datepicker:禁用多年视图
- excel - 用于重复条件合并和求和的 Excel VBA