0

I'm trying to test mailers in some system tests. I'm using Ruby 3.0.2, Rails 6.1.4, Capybara 3.26, Selenium-webdriver and I'm writing tests in Minitest. The below tests fails when it gets to the assert_equal ['dave@example.com'], mail.to line:

orders_test.rb:

test "check full payment with cheque flow" do
    LineItem.delete_all
    Order.delete_all

    visit store_index_url

    click_on 'Add to cart', match: :first
    click_on 'Checkout'

    fill_in 'order_name', with: 'Dave Thomas'
    fill_in 'order_address', with: '123 Main Street'
    fill_in 'order_email', with: 'dave@example.com'

    assert_no_selector "#order_routing_number"

    select 'Cheque', from: 'Pay type'
    fill_in 'Routing #', with: '123456'
    fill_in 'Account #', with: '678901'

    assert_selector "#order_routing_number"
    assert_selector "#order_account_number"

    perform_enqueued_jobs do
      click_button 'Place order'
    end
    
    orders = Order.all
    assert_equal 1, orders.size
    order = orders.first

    assert_equal 'Dave Thomas',       order.name
    assert_equal '123 Main Street',   order.address
    assert_equal 'dave@example.com',  order.email
    assert_equal 'Cheque',            order.pay_type 
    assert_equal 1, order.line_items.size     

    mail = ActionMailer::Base.deliveries.last
    assert_equal ['dave@example.com'],            mail.to
    assert_equal 'James Kemp<from@example.com>',  mail[:from].value
    assert_equal 'Order received; thanks',        mail.subject
  end

With the following error:

Error:
OrdersTest#test_check_full_payment_with_cheque_flow:
NoMethodError: undefined method `to' for nil:NilClass
    test/system/orders_test.rb:62:in `block in <class:OrdersTest>'

Error:
OrdersTest#test_check_full_payment_with_cheque_flow:
ActionView::Template::Error: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true

Reading error message suggestion and googling others' solutions suggests to put config.action_mailer.default_url_options = { :host => 'www.example.com' } in config/environments/test. When I put this line in it then throws an error from the same assert_equal ... mail.to line as the above error throws this error:

Error:
OrdersTest#test_check_full_payment_with_cheque_flow:
DRb::DRbRemoteError: undefined method `to' for nil:NilClass (NoMethodError)
    test/system/orders_test.rb:62:in `block in <class:OrdersTest>'

Error:
OrdersTest#test_check_full_payment_with_cheque_flow:
DRb::DRbRemoteError: No route matches {:action=>"show", :controller=>"line_items", :locale=>#<LineItem id: 1, product_id: 298486374, cart_id: nil, created_at: "2021-11-07 16:53:19.133389000 +0000", updated_at: "2021-11-07 16:53:19.966253000 +0000", quantity: 1, order_id: 980190963>}, missing required keys: [:id]
Did you mean?  line_items_url
               line_item_path
               line_items_path
               edit_line_item_url (ActionView::Template::Error)
    app/views/line_items/_line_item.html.erb:13

The code works fine in development mode, without any default_url_options explicitly specified in config anywhere.

Can anyone advise what the issue is here and how I should correct it?

(FYI - This is from the Agile Web Development with Rail 6 tutorial, ch.17.)

jbk
  • 1,911
  • 19
  • 36

1 Answers1

0

You are being told that the to method is getting called on a nil, so mail is nil. Hence there is no mail in the deliveries array.

The reason this would be occurring is that when using a Javascript driver, the Capybara tests and the Rails server run in different processes and communicate asynchronously. Hence they will each have a different deliveries array, and you cannot check emails this way.

Normally you stick to the contents of the webpage when using Capybara. You can check the database if you like, because the tests and server communicate through the database, and Capybara maintains two connections to the same database, however even there if you use database transactions and such you can get into trouble.

However in general you can't check anything that isn't the webpage or database.

You might choose to check emails through unit or request/integration tests instead, or look for a solution like the capybara-email gem if you wish to do it in a system/feature test, which does add that functionality, although I haven't used it in a while and I'm not sure how up to date it is.

matthew.tuck
  • 1,267
  • 9
  • 11