jaredlt
jaredlt

Reputation: 713

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

When I run my system test using rails test:system I receive the following error:

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>'

Background

I am writing my first system test for rails and I have a page that takes a while to load. From the Capybara documentation it recommends using the expect method eg. expect(page).to have_current_path(payment_success_url) which will use Capybara's waiting behaviour (so the test doesn't time out before the page has loaded). However, when I do this I receive the error above.

I'm a little confused as to whether expect is part of Capybara or RSpec. It's in Capybara's documentation so I assume it should just work but I'm currently stuck.

Relevant source code

# 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: "[email protected]"
    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

Details and versions used

Upvotes: 0

Views: 2136

Answers (4)

jaredlt
jaredlt

Reputation: 713

I solved this by doing the following:

  • Remove the expect line from the test (this is indeed part of RSpec and not Capybara - thanks @artur.prado)
  • Add Capybara.default_max_wait_time = 20 to application_system_test_case.rb (I set 20 because the Stripe Checkout test gateway is very slow. The beauty of Capybara being that it doesn't wait the whole 20 seconds, just 'up to' 20 seconds - it will run as soon as it is able). There may be other implications to doing this, given Capybara sets the default at 2 seconds. Given this test is interacting with a 3rd party domain I am ok with it, but buyer beware.
# 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

EDIT: Instead of setting max wait time globally, see better option in Thomas's answer.

Upvotes: 0

Omar Osama
Omar Osama

Reputation: 33

For this issue, defining matcher outside rspec should solve the problem. Adding the next two lines on top of this file will enable you to use expect method outside the context of RSpec

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: "[email protected]"
   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

Upvotes: 0

Thomas Walpole
Thomas Walpole

Reputation: 49890

As mentioned by Artur, expect is part of RSpec. If you're using minitest then you need to use the mintest assertions provided by Capybara, which you're already doing with assert_selector. The one you're missing is assert_current_path - https://rubydoc.info/github/teamcapybara/capybara/Capybara/Minitest/Assertions#assert_current_path-instance_method

assert_current_path(payment_success_url)

Since you're using a 3rd-party service directly in your tests timings are going to be slow and you may need to increase Capybaras waiting time. There are multiple ways to do that

  1. Globally Capybara.default_max_wait_time = 20 in one of your setup files This has the downside of potentially increasing time until failure in all of your tests

  2. For a block of actions. This increases the max wait time for all assertions inside the block (individual max not total)

   Capybara.using_wait_time(20) do
     assert_current_path(payment_success_url)
     assert_selector "h1", text: "Payment successful!"
   end
  1. Most calls accept a wait parameter to change the value just for that call - which probably makes the most sense in this instance

    assert_current_path(payment_success_url, wait: 20)
    

Note: you might want to look into using something like https://github.com/thoughtbot/fake_stripe rather than hitting Stripe directly during most of your tests for performance reasons

Note: you should always prefer one of the Capybara provided assertions over plain assert when dealing with anything relating to the browser in your system tests.

Upvotes: 3

artur.prado
artur.prado

Reputation: 72

expect belongs to the RSpec. It is indeed in the Capybara docs, but under the RSpec section, that's why you are getting the error message. Try to use assert instead.

Upvotes: 0

Related Questions