【问题标题】:Why am I getting the error "undefined method `expect'" when running a system test on Rails?为什么我在 Rails 上运行系统测试时收到错误“未定义的方法 `expect'”?
【发布时间】:2020-04-27 18:28:54
【问题描述】:

当我使用 rails test:system 运行系统测试时,我收到以下错误:

Error:
PaymentSessionsTest#test_payment_succesful_if_valid_details_provided:
NoMethodError: undefined method `expect' for #<PaymentSessionsTest:0x00007fffd82485d0>
Did you mean?  exec
    test/system/payment_sessions_test.rb:26:in `block in <class:PaymentSessionsTest>'

背景

我正在为 rails 编写我的第一个系统测试,并且我的页面需要一段时间才能加载。从Capybara documentation 开始,它建议使用expect 方法,例如。 expect(page).to have_current_path(payment_success_url) 将使用 Capybara 的等待行为(因此在页面加载之前测试不会超时)。但是,当我这样做时,我会收到上述错误。

我有点困惑expect 是 Capybara 还是 RSpec 的一部分。它在 Capybara 的文档中,所以我认为它应该正常工作,但我目前被卡住了。

相关源码

# test\system\payment_sessions_test.rb

require "application_system_test_case"

class PaymentSessionsTest < ApplicationSystemTestCase
  setup do
    @event = events(:one)
  end

  test "payment successful if valid details provided" do
    # Enter payment details on invite page
    visit event_url(@event)
    fill_in "Your name", with: "John Doe"
    fill_in "Email", with: "test@test.com"
    fill_in "Gift amount", with: "20"
    click_on "Pay"

    # Stripe Checkout
    fill_in "cardNumber", with: "4242424242424242"
    fill_in "cardExpiry", with: "01#{Date.today.next_year.strftime("%y")}" # grab year always as next year in 2 digit format
    fill_in "cardCvc", with: "123"
    fill_in "Name on card", with: "Mr John Doe"
    fill_in "billingPostalCode", with: "N1 7GU"
    click_on "Pay"

    # Payment success page
    expect(page).to have_current_path(payment_success_url) # ensure Capybara waits for page to load after clicking Pay
    assert_selector "h1", text: "Payment successful!"
  end

end
# test\application_system_test_case.rb

require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :chrome, screen_size: [1400, 900]
  # driven_by :selenium, using: :headless_chrome

  def setup
    # ensure url helpers use correct host and port for system tests
    Rails.application.routes.default_url_options[:host] = Capybara.current_session.server.host
    Rails.application.routes.default_url_options[:port] = Capybara.current_session.server.port
  end

end
# test\test_helper.rb

ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
require 'rails/test_help'
require 'bcrypt' # required for devise (used when creating encrypted password for user fixtures)
require 'pry'

class ActiveSupport::TestCase
  include HashidsHelper

  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
  fixtures :all

  # Add more helper methods to be used by all tests here...
  include Devise::Test::IntegrationHelpers # include devise test helpers in all tests

  setup do
    # set hashid for each event record
    # NB. can't set using fixtures as event.id is dynamic
    FixMissingHashidsService.run
  end
end
# Gemfile

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of chromedriver to run system tests with Chrome
  # gem 'chromedriver-helper'
  # Using chromedriver in Windows, added to path, via WSL1
end

使用的细节和版本

  • Rails 5.2.3
  • 通过 WSL (1) 运行并使用 Windows chromedriver 二进制文件(Chrome 打开并运行测试 - 其他不使用 expect 的测试运行良好)
  • 在测试中使用 Stripe Checkout 测试网关(即,它实际上转到 checkout.stripe.com/pay/cs_test_etc...)作为测试流程的一部分

【问题讨论】:

    标签: ruby-on-rails ruby capybara


    【解决方案1】:

    正如 Artur 所述,expect 是 RSpec 的一部分。如果您使用的是 minitest,那么您需要使用 Capybara 提供的 mintest 断言,您已经在使用 assert_selector。你缺少的是assert_current_path - https://rubydoc.info/github/teamcapybara/capybara/Capybara/Minitest/Assertions#assert_current_path-instance_method

    assert_current_path(payment_success_url)
    

    由于您在测试中直接使用第 3 方服务,因此时间会很慢,您可能需要增加 Capybaras 的等待时间。有多种方法可以做到这一点

    1. 全球 Capybara.default_max_wait_time = 20 在您的一个设置文件中 这样做的缺点是可能会增加所有测试失败的时间

    2. 对于一个动作块。这增加了块内所有断言的最大等待时间(单个最大不是总计)

       Capybara.using_wait_time(20) do
         assert_current_path(payment_success_url)
         assert_selector "h1", text: "Payment successful!"
       end
    
    1. 大多数调用都接受一个等待参数来更改该调用的值 - 这在这种情况下可能最有意义

      assert_current_path(payment_success_url, wait: 20)
      

    注意:出于性能原因,您可能希望考虑使用 https://github.com/thoughtbot/fake_stripe 之类的东西,而不是在大多数测试期间直接点击 Stripe

    注意:在系统测试中处理与浏览器相关的任何内容时,您应该始终更喜欢 Capybara 提供的断言之一,而不是普通的 assert

    【讨论】:

    • 这真的很有帮助,感谢您直接链接到相关文档。我现在意识到我正在使用 Minitest over RSpec 和 Capybara,这将有助于我之前的谷歌搜索:) 我最终使用 assert_current_path(payment_success_url, ignore_query: true) 让它工作,因为我有动态查询参数。如果您可以添加提及可能增加Capybara.default_max_wait_time 的内容,因为看起来我正在与第 3 方进行交互,以及具体设置的位置(请参阅my answer 我很乐意接受您的)。
    • @jaredlt 更新并包含比增加全局最大等待时间更好的解决方案
    • 您的回答提供的价值超出了我的预期。互联网上陌生人的慷慨让我感到谦卑!谢谢你,希望你有一个美好的一天
    【解决方案2】:

    expect 属于RSpec。它确实在 Capybara 文档中,但在 RSpec 部分下,这就是您收到错误消息的原因。尝试改用断言。

    【讨论】:

    • 我假设您的意思是使用assert current_path == payment_success_url?这不起作用,因为它需要等待页面加载。我想通过使用 have_current_path 来利用 Capybara 的等待行为
    【解决方案3】:

    对于这个问题,在rspec 之外定义matcher 应该可以解决问题。在此文件顶部添加接下来的两行将使您能够在 RSpec 的上下文之外使用 expect 方法

    require "application_system_test_case"
    require "minitest/autorun"                             # new line
    require "rspec/expectations/minitest_integration"      # new line
    class PaymentSessionsTest < ApplicationSystemTestCase
     setup do
       @event = events(:one)
     end
    
     test "payment successful if valid details provided" do
       # Enter payment details on invite page
       visit event_url(@event)
       fill_in "Your name", with: "John Doe"
       fill_in "Email", with: "test@test.com"
       fill_in "Gift amount", with: "20"
       click_on "Pay"
    
       # Stripe Checkout
       fill_in "cardNumber", with: "4242424242424242"
       fill_in "cardExpiry", with: "01#{Date.today.next_year.strftime("%y")}" # 
       grab year always as next year in 2 digit format
       fill_in "cardCvc", with: "123"
       fill_in "Name on card", with: "Mr John Doe"
       fill_in "billingPostalCode", with: "N1 7GU"
       click_on "Pay"
    
       # Payment success page
       expect(page).to have_current_path(payment_success_url) # ensure Capybara 
       waits for page to load after clicking Pay
       assert_selector "h1", text: "Payment successful!"
     end
    
    end
    

    【讨论】:

      【解决方案4】:

      我通过以下方式解决了这个问题:

      • 从测试中删除 expect 行(这确实是 RSpec 的一部分,而不是 Capybara - 感谢 @artur.prado
      • Capybara.default_max_wait_time = 20 添加到application_system_test_case.rb(我设置为 20,因为 Stripe Checkout 测试网关非常慢。Capybara 的美妙之处在于它不会等待整个 20 秒,只需“最多”20 秒 - 它将尽快运行)。考虑到 Capybara 将默认设置为 2 秒,这样做可能还有其他含义。鉴于此测试与第三方域交互,我可以接受,但买家要小心。
      # test\application_system_test_case.rb
      
      require "test_helper"
      
      class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
        driven_by :selenium, using: :chrome, screen_size: [1400, 900]
        # driven_by :selenium, using: :headless_chrome
      
        Capybara.default_max_wait_time = 20 # Stripe Checkout test gateway is slooooow
      
        def setup
          # ensure url helpers use correct host and port for system tests
          Rails.application.routes.default_url_options[:host] = Capybara.current_session.server.host
          Rails.application.routes.default_url_options[:port] = Capybara.current_session.server.port
        end
      
      end
      

      编辑:不要在全局范围内设置最大等待时间,而是在 Thomas's answer 中查看更好的选项。

      【讨论】:

        猜你喜欢
        • 2017-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-06
        • 2020-10-24
        • 1970-01-01
        • 2015-04-23
        • 1970-01-01
        相关资源
        最近更新 更多