【问题标题】:How to write integration tests for Stripe checkout on Rails?如何为 Rails 上的 Stripe 结账编写集成测试?
【发布时间】:2014-06-06 11:57:54
【问题描述】:

我在尝试为我的 Rails 3.2 应用程序编写 Stripe 的 checkout.js [https://checkout.stripe.com/checkout.js] 集成测试时碰壁了。

在手动测试时(使用 Stripe 的测试密钥),Stripe checkout 对我来说可以正常工作,但我无法让 Capybara 检测和fill_in Stripe checkout iframe 模式中的电子邮件字段。

我正在将 poltergeist 用于无头 javascript,但也使用 capybara-webkit 甚至 selenium 进行了同样的测试。

我要测试的是完整的订阅注册流程,以表明新用户可以在 Stripe 中输入他们的付款详细信息后创建订阅者帐户 - 但我无法通过 Stripe 结帐弹出窗口。

这是我的before .. do

describe "show Stripe checkout", :js => true do
  before do
    visit pricing_path
    click_button 'plan-illuminated'
    stripe_iframe = all('iframe[name=stripe_checkout_app]').last
    Capybara.within_frame stripe_iframe do        
      fill_in "email", :with => "test-user@example.com"
      fill_in "billing-name", :with => "Mr Name"
      fill_in "billing-street", :with => "test Street"
      fill_in "billing-zip", :with => 10000
      fill_in "billing-city", :with => "Berlin"
      click_button "Payment Info"
    end
  end
  it { should have_selector('button', text: "Subscribe") }
end

哪些错误:

Failure/Error: Capybara.within_frame stripe_iframe do
 Capybara::Poltergeist::TimeoutError:
   Timed out waiting for response to {"name":"push_frame","args":[null]}

如果我换掉尝试选择正确的 iframe(此处建议:Capybara trouble filling in JS modal),如下所示:

# stripe_iframe = all('iframe[name=stripe_checkout_app]').last
# Capybara.within_frame stripe_iframe do  
Capybara.within_frame 'stripe_checkout_app' do

我仍然得到类似的:

Capybara::Poltergeist::TimeoutError:
   Timed out waiting for response to {"name":"push_frame","args":["stripe_checkout_app"]}

看来,无论我使用哪个 javascript 测试 gem,rspec/capybara 都找不到 Stripe checkout iframe。当我检查 Selenium 时,我看到按下了 Choose this Plan 按钮并弹出了 Checkout 弹出窗口,但规范在寻找要填写的电子邮件字段时超时。

有什么想法吗?

我已经试过了:

  • 选择或查找电子邮件字段的各种方式。
  • 更新我所有的宝石。
  • 在此之前使用 StripeMock(不应该涉及它,对吧?)。
  • 对 Stripe 自己的网站运行相同的测试,结果相同:

测试:

  visit "https://stripe.com/docs/checkout"
  click_button 'Pay with Card'
  stripe_iframe = all('iframe[name=stripe_checkout_app]').last
  Capybara.within_frame stripe_iframe do
    fill_in 'Email', with: 'test@example.com'
    sleep 3
  end

根据我用来选择 iframe 的方法,我会收到相同的错误。仅使用Capybara.within_frame 'stripe_checkout_app' do:

 Failure/Error: Capybara.within_frame stripe_iframe do
 Capybara::Poltergeist::TimeoutError:
   Timed out waiting for response to {"name":"push_frame","args":[null]}

或将 Selenium 与 stripe_iframe = all('iframe[name=stripe_checkout_app]').last 一起使用:

 Failure/Error: Unable to find matching line from backtrace
 SystemStackError:
   stack level too deep

甚至只是:

 Failure/Error: fill_in 'Email', with: 'test@example.com'
 Capybara::ElementNotFound:
   cannot fill in, no text field, text area or password field with id, name, or label 'Email' found

...取决于我使用的测试 javascript gem。

非常感谢任何帮助或智慧!

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 testing capybara stripe-payments


    【解决方案1】:

    到目前为止,我无法在这里找到任何适合我的解决方案,然后阅读这篇文章:https://gist.github.com/nruth/b2500074749e9f56e0b7 我意识到关键是在测试中添加延迟以给 Stripe 足够的时间 1)加载结帐窗口并 2) 处理令牌。

    出于这个原因,我可以开始工作的唯一代码是这个(随意玩时间):

    describe "test stripe" do, js: true, driver: :selenium do
    
      before do
        ... # fill in order form or whatever
        click_button "checkout_with_stripe"
        sleep(2) # allows stripe_checkout_app frame to load
        stripe_iframe = all('iframe[name=stripe_checkout_app]').last
        Capybara.within_frame stripe_iframe do
          page.execute_script(%Q{ $('input#email').val('jamesdd9302@yahoo.com'); })
            page.execute_script(%Q{ $('input#card_number').val('4242424242424242'); })
            page.execute_script(%Q{ $('input#cc-exp').val('08/44'); })
            page.execute_script(%Q{ $('input#cc-csc').val('999'); })
            page.execute_script(%Q{ $('#submitButton').click(); })
          sleep(3) # allows stripe_checkout_app to submit
        end
      end
    
      it "should successfully process the request" do
         ... # test should pass
      end 
    end
    

    对于 Capybara-webkitsleep 技巧不起作用,可悲的是,@Ryan 的解决方案也不起作用,我厌倦了试图解决这个问题,所以我就停下来了,但希望有人否则会得到它,因为出于速度原因我宁愿使用 webkit! (我用的是capybara-webkit 1.3.0

    如果有帮助,这里是我的相关版本:

    selenium-webdriver 2.35.1
    rspec-rails 2.14.2
    capybara 2.1.0
    stripe 1.16.0 da216fd
    rails 4.1.1
    ruby 2.1.2
    

    【讨论】:

    • 这很好用,但是我确实增加了一点睡眠,而不是将代码放在前块中,而是将其放入实际场景中。无论哪种方式,很好的解决方案。谢谢。
    • 赞成使用Capybara.default_wait_time = 2 而不是sleep(2)
    • 对我来说唯一可行的解​​决方案。可惜我需要硒来做这个测试!
    【解决方案2】:

    问题解决了!

    parov 的一些帮助下,通过他的类似问题和答案 [Poltergeist Stripe checkout.js] 我将问题追踪到使用旧版本的 Capybara '~>1.1.2' 和随后的依赖项 -这对各种 javascript 测试 gem(即 selenium、capybara-webkit、poltergeist...)产生了影响。

    将 Capybara 的 bundle update 升级到 2.3.0,因此将 poltergeist 升级到 1.5.1,使用 2.42.0 的 selenium-webdriver 和 1.1.0 的 capybara-webkit,让(几乎)一切正常。

    对于 selenium,此方法通过 Capybara trouble filling in JS modal确实 工作:

      stripe_iframe = all('iframe[name=stripe_checkout_app]').last
      Capybara.within_frame stripe_iframe do
        fill_in "email", with: "test-user@example.com"
        ...
      end
    

    但是,这在 poltergeist 或 capybara-webkit 中不起作用。

    对于 poltergeist,parov 的建议有效:

      stripe = page.driver.window_handles.last
      page.within_window stripe do
        fill_in "email", with: "test-user@example.com"
        ...
      end
    

    对于 capybara-webkit,我无法得到任何工作,但考虑到我与 poltergeist 合作,我没有花太多时间寻找 capybara-webkit 解决方案。 p>

    评论?

    【讨论】:

    • Selenium 版本非常感谢,但 poltergeist 版本不工作,它实际上卡在那里。
    • 是的,我的也停止使用 ?最近的宝石更新。将需要再次调查... grrr。
    • 这似乎不适用于最近的 gem 版本(我也在使用 Poltergeist),它无法为我找到“卡号”。有更新吗?
    【解决方案3】:

    我已经为此奋斗了一段时间(正如要点 james 提到的那样),最后发现它太脆弱且太慢而无法测试结帐 js。

    相反,您可以使用以下代码(ruby、capybara、selenium)来存根 checkout.js 并使用条带令牌发布您的表单:

      # optionally ensure the iframe was opened
      expect(page).to have_css('iframe[name="stripe_checkout_app"]')
      # post the form
      token = Stripe::Token.create(
        :card => {
          :number => "4242424242424242",
          :exp_month => 7,
          :exp_year => 2019,
          :cvc => "314",
          address_line1: Faker::Address.street_address,
          address_city: Faker::Address.city,
          address_zip: Faker::Address.postcode,
          address_country: 'United Kingdom'
        },
      )
      page.execute_script("$('#payment_token').val('#{token.id}');")
      page.execute_script("$('#our-stripe-payment-form').submit();")
    

    n.b.这假设您已经在您的测试环境中(通过您的 rails 应用程序)加载了 stripe api gem,并且拥有注册的 API 密钥等,否则 see the docs

    【讨论】:

    • 谢谢 nruth,我得试试看。
    • 这个解决方案看起来不错,但我在 Stripe::Token.create 上收到以下错误:“Stripe::InvalidRequestError: You must verify a phone number on your Stripe account before you can send raw credit card numbers到 Stripe API。您可以通过使用 Stripe.js、Stripe 移动绑定或 Stripe Checkout 来避免此要求。有关更多信息,请参阅 dashboard.stripe.com/phone-verification。"
    • @JunichiIto 这听起来像是您的 Stripe 帐户而不是测试方法的问题。我建议您检查您使用的不是真实的测试 API 密钥,并按照他们的要求检查您的帐户。
    • @nruth 你是对的。我在为测试代码苦苦挣扎时收到了一封电话验证邮件?
    【解决方案4】:

    我尝试了 James 的答案并针对我当前的环境进行了修改。这是我的代码(Chrome 无头系统规范):

    require 'rails_helper'
    
    describe 'Orders', type: :system do
      before do
        # Temporarily change default_max_wait_time to wait for Stripe response
        @old_wait_time = Capybara.default_max_wait_time
        Capybara.default_max_wait_time = 10
      end
    
      after do
        Capybara.default_max_wait_time = @old_wait_time
      end
    
      scenario 'Payment via Stripe', js: true do
        visit payment_path
        click_button 'Pay with Card'
    
        # Use VCR to avoid actual data creation
        VCR.use_cassette 'orders/payment_via_stripe' do
          expect(page).to have_css('iframe[name="stripe_checkout_app"]')
          stripe_iframe = all('iframe[name=stripe_checkout_app]').last
    
          Capybara.within_frame stripe_iframe do
            # Set values by placeholders
            fill_in 'Card number', with: '4242424242424242'
            fill_in 'MM / YY', with: '08/44'
            fill_in 'CVC', with: '999'
            # You might need to fill more fields...
    
            click_button 'Pay $9.99'
          end
    
          # Confirm payment completed
          expect(page).to have_content 'Payment completed.'
        end
      end
    end
    

    我正在使用:

    • selenium-webdriver 3.14.0
    • rspec-rails 3.8.0
    • 水豚3.7.1
    • 条带化 3.26.0
    • 导轨 5.2.1
    • 红宝石 2.5.1
    • vcr 4.0.0
    • webmock 3.4.2
    • 铬 69

    我的应用是根据https://stripe.com/docs/checkout/rails 构建的。

    【讨论】:

      【解决方案5】:

      对于 capybara-webkit,我能够让它工作:

        stripe_iframe = page.driver.window_handles.last
        page.within_window stripe_iframe do
          fill_in "email", with: "test-user@example.com" 
          ...
        end
      

      【讨论】:

      • 谢谢瑞恩。这与我的 poltergeist 代码相同,所以我再试一次。总的来说,我发现 poltergeist 比 capybara-webkit 更好 - 尽管现在有 gems 和所有它们的依赖项......!
      最近更新 更多