1

我们正在添加 PayPal Express Checkout 作为签出在 Rails 2.3.18 上运行的电子商务应用程序的选项。我已经为我的自定义PayPal::Merchant::ExpressCheckout模块完成了代码工作和一些单元测试,但是我无法理解如何正确地模拟或存根控制器方法,以便我可以编写集成测试。

我面临的一个问题是所有 PayPal API 调用都指向同一个端点 URI,在 POST 数据中只有一个动作参数来区分我们正在调用的动作。虽然我已成功设置 FakeWeb 以在我的单元测试中模拟来自 PayPal API 的正确 XML 响应,但在某些集成场景中,我需要能够处理背靠背的 API 请求。有没有办法告诉 FakeWeb 根据发布的数据做出不同的响应?或者,有没有办法让 FakeWeb 在拦截第一个请求后触发回调方法,以便我可以设置下一个请求?

另一个问题是如何模拟到 PayPal 的重定向。现在,用户单击我们网站上的 Checkout With PayPal 按钮,该按钮将他们重定向到setupmy 上的方法ExpressCheckoutsController,该方法获取令牌并设置结帐 URL,然后将用户重定向到那里。我需要在集成测试中模拟两个场景: 1. 用户正确提交表单并发送到我的返回 URL 2. 用户取消并发送到我的取消 URL 有没有办法在不重写整个 ExpressCheckoutsController 的情况下做到这一点测试文件中的类?

万一这很重要,我们使用的是 paypal-sdk-merchant gem。我们的测试环境使用以下 gem:

group :test do
  gem 'autotest-rails', '4.1.0'
  gem 'ZenTest', '< 4.6'
  gem 'fakeweb', '1.2.6'
  gem 'mocha', '0.9.4'
  gem 'quietbacktrace', '0.1.1'
  gem 'factory_girl', '1.2.0'
  gem 'thoughtbot-shoulda', '2.10.2', :require => 'shoulda'
  gem 'nokogiri', '1.5.6'
  gem 'webrat', '0.4.4'
end

更新

我能够通过使用 Mocha 来存根我的自定义ExpressCheckout模块的express_checkout_url方法来解决重定向问题,以便它简单地重定向到returnorcancel操作。

PayPal::Merchant::ExpressCheckout.any_instance.stubs(:express_checkout_url).returns(return_order_express_checkout_path)
4

1 回答 1

1

我了解到 FakeWeb 支持旋转响应,虽然这有助于一些集成测试步骤,但我仍然 遇到了 API 方法在和调用GetExpressCheckoutDetails之间被调用次数不确定的实例,具体取决于其他几个因素。而不是试图弄清楚我需要伪造多少响应,并且在挖掘了很多库之后,我最终在我的集成测试类中使用了这个辅助方法:SetExpressCheckoutDoExpressCheckoutPaymentPayPal::SDK::Core

def stub_express_checkout
  api = PayPal::SDK::Merchant::API.new

  PayPal::Merchant::ExpressCheckout.any_instance.stubs(:express_checkout_url).returns(return_order_express_checkout_path)

  FakeWeb.register_uri(
    :post,
    api.service_endpoint,
    :content_type => "application/xml",
    :status => ["200", "OK"],
    :body => ""
  )

  PayPal::Merchant::ExpressCheckout.any_instance.stubs(:set_express_checkout).returns(
    PayPal::SDK::Merchant::DataTypes::SetExpressCheckoutResponseType.new(api.format_response({
      :response => FakeWeb.response_for(:post, api.service_endpoint).tap { |resp| 
        resp.instance_variable_set("@body", File.read(Rails.root.join("test/fixtures/express_checkout/success/set.xml")))
      }
    })[:data])
  )

  PayPal::Merchant::ExpressCheckout.any_instance.stubs(:get_express_checkout).returns(
    PayPal::SDK::Merchant::DataTypes::GetExpressCheckoutDetailsResponseType.new(api.format_response({
      :response => FakeWeb.response_for(:post, api.service_endpoint).tap { |resp| 
        resp.instance_variable_set("@body", File.read(Rails.root.join("test/fixtures/express_checkout/success/get.xml")))
      }
    })[:data])
  )

  PayPal::Merchant::ExpressCheckout.any_instance.stubs(:do_express_checkout).returns(
    PayPal::SDK::Merchant::DataTypes::DoExpressCheckoutPaymentResponseType.new(api.format_response({
      :response => FakeWeb.response_for(:post, api.service_endpoint).tap { |resp| 
        resp.instance_variable_set("@body", File.read(Rails.root.join("test/fixtures/express_checkout/success/do.xml")))
      }
    })[:data])
  )

  FakeWeb::Registry.instance.uri_map[FakeWeb::Registry.instance.send(:normalize_uri, api.service_endpoint)] = {}
end

这很难看,而且我不喜欢依赖这么多内部PayPal::SDK::Core方法来存根我的模块方法,但是在我尝试的 2 打方法中,这是最终奏效的方法。

于 2013-10-24T00:58:47.593 回答