当 运行 在 Rails 上进行系统测试时,为什么我会收到错误 "undefined method `expect'"?

Why am I getting the error "undefined method `expect'" when running a system test on Rails?

当我 运行 我的系统测试使用 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 是水豚还是 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

详细信息和使用的版本

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

正如 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

我通过执行以下操作解决了这个问题:

  • 从测试中删除 expect 行(这确实是 RSpec 的一部分,而不是 Capybara - 谢谢
  • Capybara.default_max_wait_time = 20添加到application_system_test_case.rb(我设置为20是因为Stripe Checkout测试网关非常慢。水豚的美妙之处在于它不会等待整整20秒,只是'up to' 20 秒 - 它会尽快 运行)。考虑到 Capybara 将默认设置为 2 秒,这样做可能还有其他含义。鉴于此测试与第 3 方域进行交互,我对此表示满意,但买家要当心。
# 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

编辑:不要全局设置最长等待时间,而是在 中查看更好的选项。

对于这个问题,在 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