如何在Rails 2.3应用程序中使用TestUnit为PayPal Express签出配置集成测试

如何在Rails 2.3应用程序中使用TestUnit为PayPal Express签出配置集成测试,paypal,integration-testing,ruby-on-rails-2,testunit,Paypal,Integration Testing,Ruby On Rails 2,Testunit,我们正在添加PayPal Express Checkout,作为在Rails 2.3.18上运行的ecom应用程序的签出选项。我的自定义PayPal::Merchant::ExpressCheckout模块的代码正在运行,并进行了一些单元测试,但我在理解如何正确模拟或存根控制器方法以编写集成测试方面遇到了困难 我面临的一个问题是,所有PayPal API调用都指向同一个端点URI,在发布的数据中只有一个操作参数来区分我们调用的是哪个操作。虽然我已经成功地在单元测试中设置了FakeWeb来模拟来自

我们正在添加PayPal Express Checkout,作为在Rails 2.3.18上运行的ecom应用程序的签出选项。我的自定义
PayPal::Merchant::ExpressCheckout
模块的代码正在运行,并进行了一些单元测试,但我在理解如何正确模拟或存根控制器方法以编写集成测试方面遇到了困难

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

另一个问题是如何模拟重定向到PayPal。现在,用户单击我们网站上的“使用贝宝结账”按钮,将他们重定向到my
ExpressCheckoutsController
上的
setup
方法,该方法获取令牌并设置结账URL,然后将用户重定向到那里。我需要在集成测试中模拟两个场景: 1.用户正确提交表单并发送到我的返回URL 2.用户取消并被发送到我的取消URL 有没有一种方法可以做到这一点而不必在测试文件中重写整个ExpressCheckoutsController类

如果有关系,我们使用的是paypal sdk商户gem。我们的测试环境使用以下gems:

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

我了解到FakeWeb支持,虽然这在一些集成测试步骤中有所帮助,但我仍然遇到这样的情况,即
GetExpressCheckoutDetails
API方法在
SetExpressCheckout
DoExpressCheckoutPayment
调用之间被调用的次数不确定,这取决于其他几个因素。我没有试图找出需要伪造多少响应,而是在深入研究了大量的
PayPal::SDK::Core
库之后,在我的集成测试类中使用了以下helper方法:

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
方法来存根我的模块方法,但在我尝试的20多种方法中,这一种终于奏效了。

我了解到FakeWeb支持,虽然这在一些集成测试步骤中有所帮助,我仍然遇到过这样的情况,即
GetExpressCheckoutDetails
API方法在
SetExpressCheckout
DoExpressCheckoutPayment
调用之间被调用的次数不确定,这取决于其他几个因素。我没有试图找出需要伪造多少响应,而是在深入研究了大量的
PayPal::SDK::Core
库之后,在我的集成测试类中使用了以下helper方法:

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
方法来存根我的模块方法,但是在我尝试过的20多种方法中,这一种终于奏效了。

你有set.xml、get.xml和Do.xml的例子吗?我喜欢你的解决方案!自从我最初发布我的解决方案以来,我已经显著地更新了我的测试。这是所有活动部件的要点。您有set.xml、get.xml和Do.xml的示例吗?我喜欢你的解决方案!自从我最初发布我的解决方案以来,我已经显著地更新了我的测试。这是所有活动部件的要点。