6

情况:使用 Rspec、FactoryGirl 和 VCR 测试 rails 应用程序。

每次创建用户时,都会通过 Stripe 的 API 创建关联的 Stripe 客户。在测试时,在涉及用户创建的每个规范中添加一个VCR.use_cassetteor并没有真正的意义。describe "...", vcr: {cassette_name: 'stripe-customer'} do ...我的实际解决方案如下:

RSpec.configure do |config|
  config.around do |example|
    VCR.use_cassette('stripe-customer') do |cassette|
      example.run
    end
  end
end

但这是不可持续的,因为每个 http 请求都会使用相同的磁带,这当然是非常糟糕的。

问题:如何根据个人要求使用特定的固定装置(磁带),而不为每个规格指定磁带?

我有这样的想法,伪代码:

stub_request(:post, "api.stripe.com/customers").with(File.read("cassettes/stripe-customer"))

相关代码(作为要点):

# user_observer.rb

class UserObserver < ActiveRecord::Observer

  def after_create(user)
    user.create_profile!

    begin
      customer =  Stripe::Customer.create(
        email: user.email,
        plan: 'default'
        )

      user.stripe_customer_id = customer.id
      user.save!
    rescue Stripe::InvalidRequestError => e
      raise e
    end

  end
end


# vcr.rb

require 'vcr'

VCR.configure do |config|
  config.default_cassette_options = { record: :once, re_record_interval: 1.day }
  config.cassette_library_dir = 'spec/fixtures/cassettes'
  config.hook_into :webmock
  config.configure_rspec_metadata!
end


# user_spec.rb

describe :InstanceMethods do
  let(:user) { FactoryGirl.create(:user) }

  describe "#flexible_name" do
    it "returns the name when name is specified" do
      user.profile.first_name = "Foo"
      user.profile.last_name = "Bar"

      user.flexible_name.should eq("Foo Bar")
    end
  end
end

编辑

我结束了这样的事情:

VCR.configure do |vcr|
  vcr.around_http_request do |request|

    if request.uri =~ /api.stripe.com/
      uri = URI(request.uri)
      name = "#{[uri.host, uri.path, request.method].join('/')}"
      VCR.use_cassette(name, &request)

    elsif request.uri =~ /twitter.com/
      VCR.use_cassette('twitter', &request)
    else
    end

  end
end
4

3 回答 3

14

VCR 2.x 包含一个专门用于支持以下用例的功能:

https://relishapp.com/vcr/vcr/v/2-4-0/docs/hooks/before-http-request-hookhttps://relishapp.com/vcr/vcr/v/2-4-0/docs/hooks/after-http-request-hookhttps://relishapp.com/vcr/vcr/v/2-4-0/docs/hooks/around-http-request-hook

VCR.configure do |vcr|
  vcr.around_http_request(lambda { |req| req.uri =~ /api.stripe.com/ }) do |request|
    VCR.use_cassette(request.uri, &request)
  end
end
于 2013-05-14T05:21:26.467 回答
2

IMO,像这样的库应该为您提供一个模拟类,但是 w/e.


您已经可以使用Webmock来完成您的伪代码示例,它是 VCR 使用的默认 Internet 模拟库。

body = YAML.load(File.read 'cassettes/stripe-customer.yml')['http_interactions'][0]['response']['body']['string']
stub_request(:post, "api.stripe.com/customers").to_return(:body => body)

您可以将其放在仅在特定标签上运行的 before 块中,然后标记进行 API 调用的请求。


在他们的测试中,他们覆盖了委托给 RestClient ( link ) 的方法。你也可以这样做,看看他们的测试套件,看看他们是如何使用它的,特别是他们对 test_response 的使用。我认为这是一种非常老套的做事方式,并且会对此感到非常不舒服(请注意,我是少数有这种不适的人)但它现在应该可以工作(它有可能在你不知道的情况下中断,直到运行时)。如果我要这样做,我想为两个模拟构建真实对象(一个模拟休息客户端,另一个模拟休息客户端响应)。

于 2013-05-14T02:49:01.403 回答
-1

VCR 的全部意义(大多数情况下)只是为了重放先前请求的响应。如果您在那里挑选和选择什么响应可以返回到什么请求,那么您就是引用/取消引用做错了。

就像 Joshua 已经说过的,你应该使用 Webmock 来做这样的事情。无论如何,这就是 VCR 在幕后使用的东西。

于 2013-05-14T06:41:02.807 回答