【发布时间】:2021-10-31 18:37:49
【问题描述】:
背景:我正在实现一个已有 6 年历史的 Rails 项目,从那时起就没有接触过框架。因此,我正在重新学习很多东西。
我正在尝试了解模拟需要同步完成的 API 调用的最佳方法。 Order 有一个 Invoice,并且 Invoice必须从外部服务获取参考。没有发票,订单就毫无用处。
以下是该应用程序的简单版本。 Order 模型是应用程序的核心。
开放式问题:
- 在 spec_helper.rb 中全局模拟 SDK 的最佳做法是什么?其中将包含我的
allow_any_instance_of(InvoiceServiceSdk) - 我有一个 Order 工厂,在我的测试中几乎无处不在。但是我很困惑我是否也可以在 Invoice 工厂中循环。 FactoryBot 现在对我来说很陌生。
# app/models/order.rb
class Order < ApplicationRecord
has_one :invoice, autosave: true
before_create :build_invoice
def build_invoice
self.invoice = Invoice.new
end
end
# app/models/invoice.rb
class Invoice < ApplicationRecord
belongs_to :order
before_create :generate
def generate
invoice_service = InvoiceServiceSdk.new
self.external_id = invoice_service.fetch
end
end
# app/models/invoice_service_sdk.rb
require 'uri'
require 'net/http'
class InvoiceServiceSdk
def fetch
uri = URI('https://example.com/') # Real HTTP request
res = Net::HTTP.get_response(uri)
SecureRandom.urlsafe_base64 # "ID" that API "provides"
end
end
# spec/models/order.rb
require 'rails_helper'
RSpec.describe Order, type: :model do
before do
allow_any_instance_of(InvoiceServiceSdk).to receive(:fetch).and_return('super random external invoice ID')
end
context "new order + invoice" do
it {
o = Order.new
o.save
expect(o.invoice.external_id).to eq 'super random external invoice ID'
}
end
end
【问题讨论】:
-
您应该将其缩小到一个问题。
allow_any_instance_of被认为是代码异味,RSpec 团队不建议在遗留代码之外使用它。您应该在您的服务对象上创建一个可以被存根的工厂方法。从几天前看到这个答案到一个类似的问题stackoverflow.com/a/68993724/544825 -
可以使用 webmock 和 VCR gems 来存根 HTTP。
-
@max 我在整个测试过程中都在使用 WebMock 和 VCR。在 spec_helper 中 Stub 请求是不是很臭?
-
我真的鼓励你创建一个关于 FactoryBot 的单独问题。
-
工厂模式对我来说是新的。我在全球范围内使用 WebMocking 端点。
标签: ruby-on-rails ruby rspec factory-bot