我有一个关于如何在示例之间共享rspec-mocks的double的问题。我正在用rspec-mocks 3.1.3
编写一个新的rails应用程序。我习惯于使用旧的(〈2.14和,并试图更新我对当前rspec使用情况的了解。
我有一个模型方法:
def self.from_strava(activity_id, race_id, user)
@client ||= Strava::Api::V3::Client.new(access_token: 'abc123')
activity = @client.retrieve_an_activity(activity_id)
result_details = {race_id: race_id, user: user}
result_details[:duration] = activity['moving_time']
result_details[:date] = Date.parse(activity['start_date'])
result_details[:comment] = activity['description']
result_details[:strava_url] = "http://www.strava.com/activities/#{activity_id}"
Result.create!(result_details)
end
下面是它的规格:
describe ".from_strava" do
let(:user) { FactoryGirl.build(:user) }
let(:client) { double(:client) }
let(:json_response) { JSON.parse(File.read('spec/support/strava_response.json')) }
before(:each) do
allow(Strava::Api::V3::Client).to receive(:new) { client }
allow(client).to receive(:retrieve_an_activity) { json_response }
allow(Result).to receive(:create!)
end
it "sets the duration" do
expect(Result).to receive(:create!).with(hash_including(duration: 3635))
Result.from_strava('123', 456, user)
end
it "sets the date" do
expect(Result).to receive(:create!).with(hash_including(date: Date.parse("2014-11-14")))
Result.from_strava('123', 456, user)
end
end
当我运行一个单独的测试时,它是正常的,但是当我运行整个describe ".from_strava"
块时,它失败并显示以下消息
Double :client was originally created in one example but has leaked into another example and can no longer be used. rspec-mocks' doubles are designed to only last for one example, and you need to create a new one in each example you wish to use it for.
型
我理解它的意思,但是这确实是在两个例子中使用double
的一个恰当的用法,毕竟,client
double对这个例子来说并不重要,这只是我加载固定响应的一种方式。我想我可以使用WebMock,但这似乎非常低级,不能很好地转换为实际编写的代码。毕竟,每个例子我们只能Assert一件事。
我曾考虑过将client
double替换为调用
allow(Strava::Api::V3::Client).to receive_message_chain(:new, :retrieve_an_activity) { json_response }
但这似乎也不是正确的方法,因为文档指出receive_message_chain
应该是一种代码气味。
因此,如果我不应该使用receive_message_chain
,共享client
double,也不应该遵循标准的DRY原则,那么我应该如何解决这个问题?
我很想得到一些反馈。
谢谢,戴夫
4条答案
按热度按时间tgabmvqs1#
为外部组件缓存客户端通常是非常必要的(保持连接/您可能需要的任何SSL设置等),而为了修复测试问题而删除它并不是一个理想的解决方案。
为了修复测试(而不重构代码),可以执行以下操作以在每次测试后清除示例变量:
诚然,这不是最干净的解决方案,但它似乎是最简单的,并且实现了这两个目标,让您有一个清晰的设置,在测试之间没有状态共享,并保持客户机在“正常”操作模式下缓存。
oug3syen2#
当然,这是在2个示例中使用的双精度的适当使用。
不,不是。:)您尝试使用类变量;不要这样做,因为变量不跨越示例。解决方案是每次设置客户端,即在每个示例中。
不良:
良好:
hrysbysz3#
我在自己的一个应用程序中也有同样的用例,我们通过将缓存提取到一个私有方法中,然后存根该方法以返回double(而不是直接存根
new
方法)来解决这个问题。例如,在测试的类中:
在规格中:
3phpmpom4#
TLDR:添加
after { order.vendor_service = nil }
以平衡before
块。或者继续阅读...我遇到了这个问题,它的来源并不明显。在order_spec.rb模型测试中,我得到了这个:
在我的
Order
模型中:当我只在order_spec.rb上运行rspec时,这种方法运行得很好
我在order_controller_spec.rb中模拟了一些完全不同的东西,使用了
allow_any_instance_of()
而不是double
和allow
:型
这个测试也很好。
令人困惑的问题是,当我运行完整的测试套件时,我在控制器模拟上得到了OP的错误--使用
allow_any_instance
的那个。这很难跟踪,因为问题(或者至少是我的解决方案)存在于我使用double/allow
的模型测试中。为了解决这个问题,我添加了一个
after
块来清除类变量@@vendor_service,从而平衡before
块的操作:这迫使
||= VendorAPI.new()
在随后的不相关测试中使用真实的的new
函数,而不是模拟对象。