rspec-mocks的双打仅设计为一个例子

dav*_*ris 11 ruby unit-testing rspec ruby-on-rails rspec-mocks

我有一个关于如何在示例之间分享rspec-mocks的双重问题.我正在用rspec-mocks编写一个新的rails应用程序3.1.3.我习惯使用旧的(<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
Run Code Online (Sandbox Code Playgroud)

以下是规范:

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
Run Code Online (Sandbox Code Playgroud)

当我自己运行单个测试时它很好,但是当我运行整个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.
Run Code Online (Sandbox Code Playgroud)

我理解它的含义,但肯定这是double在2个例子中使用的适当用法.毕竟,clientdouble对示例来说并不重要,它只是我加载预设响应的一种方式.我想我可以使用WebMock,但这似乎非常低级,并且不能很好地转换为实际编写的代码.毕竟,我们应该只为每个例子断言一件事.

我曾考虑过用client一个电话取代双人

allow(Strava::Api::V3::Client).to receive_message_chain(:new, :retrieve_an_activity) { json_response }
Run Code Online (Sandbox Code Playgroud)

但这似乎也不是正确的方法,因为文档说明 receive_message_chain应该是代码气味.

所以,如果我不使用receive_message_chain,共享client双重并遵循标准DRY原则那么我应该如何解决这个问题呢?

我会喜欢这方面的一些反馈.

谢谢,戴夫

joe*_*son 8

当然,这是在两个例子中使用的双重的适当使用.

不,这不对.:)你正在尝试使用类变量; 不这样做,因为变量不跨越示例.解决方案是每次设置客户端,即在每个示例中.

坏:

@client ||= Strava::Api::V3::Client.new(access_token: 'abc123')
Run Code Online (Sandbox Code Playgroud)

好:

@client = Strava::Api::V3::Client.new(access_token: 'abc123')
Run Code Online (Sandbox Code Playgroud)


Mar*_*iej 6

我不喜欢这个答案。通常经常需要为外部组件缓存客户端(保持活动连接/您可能需要的任何SSL设置,等等),并且为了解决测试问题而删除它不是理想的解决方案。

为了修复您的测试(无需重构代码),您可以执行以下操作在每个测试之后清除实例变量:

after { Result.instance_variable_set("@client", nil) }
Run Code Online (Sandbox Code Playgroud)

公认的是,这不是最干净的解决方案,它似乎是最简单的解决方案,并且可以同时实现这两种解决方案,它使您可以进行清晰的设置,并且测试之间没有共享状态,并使客户端缓存在“正常”操作模式下。