My intention is to create a few tests for the same form, and in these tests I need Capybara to fill the form completely. Still, I wanted to avoid rewriting the code for filling the form.
I'm not using RSPEC! I'm using minitest.
The problem I'm facing is that the Capybara method visit
and the Warden helper method login_as
are not accessible from my CapybaraHelper
module.
I've seen this question, so I created my module in the test/support
folder, but the methods I mentioned still are not accessible.
How to reuse code in Capybara
I've seen this question, but the code I want to reuse doesn't seem to fit well in the setup
method nor in the teardown
method.
Rails: Premake a model instance for your unit tests (for DRY purposes)?
I've also seen posts saying this module should be inside test_helper.rb
, but as I add more modules to this file it will get messy.
So now I'm wondering what I'm doing wrong. I've tried adding the following lines to CapybaraHelper
but it didn't help. It actually raised the error NoMethodError: undefined method
setup' for CapybaraHelper:Module`.
include Devise::Test::ControllerHelpers
include Warden::Test::Helpers
Is it the right way to reuse code in tests? Am I missing something that should be included in my helper module? All those methods work perfectly in the test controller that is using CapybaraHelper:Module
.
Here is the error message:
NoMethodError: undefined method `login_as' for CapybaraHelper:Module
And here is an error message from another test using CapybaraHelper:Module
.
NoMethodError: undefined method `visit' for CapybaraHelper:Module
Here's my test:
require 'test_helper'
class TravelsControllerTest < ActionController::TestCase
include Devise::Test::ControllerHelpers
include Warden::Test::Helpers
Warden.test_mode!
test 'should accept correct fields' do
CapybaraHelper.login
result = CapybaraHelper.access_and_fill_travel_page
assert_equal "/travels/success/#{Travel.last.uuid}", result[:final_path]
end
end
And here's the helper I created in test/support/capybara/capybara_helper.rb
to avoid code duplication:
require 'test_helper'
require 'capybara/rails'
require 'capybara/poltergeist'
module CapybaraHelper
def self.access_and_fill_travel_page options = {}
options.symbolize_keys!
set_js_driver
visit(Rails.application.routes.url_helpers.root_path)
initial_path = current_path
#Fields
fill_in('origin', with: options[:origin] || 'Guarulhos')
fill_autocomplete('origin', with: options[:origin] || 'Guarulhos')
fill_in('destination', with: options[:destination] || 'Seul')
fill_autocomplete('destination', with: options[:destination] || 'Seul')
fill_in('date_from', with: options[:date_from] || Date.today+10)
fill_in('date_to', with: options[:date_to] || Date.today+26)
fill_in('adults', with: options[:adults] || 1)
fill_in('children', with: options[:children] || 0)
fill_in('babies', with: options[:babies] || 0)
find('#travel-submit').click()
final_path = current_path
return {initial_path: initial_path, final_path: final_path}
end
def self.fill_autocomplete(field, options = {})
page.execute_script %Q{ el = $('input[name=#{field}]').get(0) }
page.execute_script %Q{ $(el).trigger('focus') }
page.execute_script %Q{ $(el).trigger('keydown') }
page.all('.ui-menu-item', minimum: 1)
page.execute_script %Q{ item = $('.ui-menu-item').get(0) }
page.execute_script %Q{ $(item).trigger('mouseenter').click() }
end
def self.set_js_driver
Capybara.javascript_driver = :poltergeist
Capybara.current_driver = Capybara.javascript_driver
end
def self.login
user = FactoryGirl.create(:user)
login_as(user, :scope => :user)
end
end
You should use ActionDispatch::IntegrationTest
and not ActionController::TestCase
as the parent class for tests using capybara. ActionController::TestCase
mocks out the request phase and large parts of Rails. It is depreciated in Rails 5.
Instead of calling methods on your test helper module you should mix them into your test classes.
class TravelsIntegrationTest < ActionDispatch::IntegrationTest
include Devise::Test::ControllerHelpers
include Warden::Test::Helpers
include CapybaraHelper
Warden.test_mode!
test 'should accept correct fields' do
login
# ...
end
end
module CapybaraHelper
def login(user = FactoryGirl.create(:user))
login_as(user, scope: :user)
end
end
Apart from that you are lacking in code organization - setup steps like setting Warden.test_mode!
should be done in your test_helper.rb
not repeated across your tests. Don't throw all your step definitions into a single file either. You can place them in /test/support/
for example.
module SessionHelper
def login(user = FactoryGirl.create(:user))
login_as(user, :scope => :user)
end
end
module TravelHelper
def access_and_fill_travel_page
# ...
end
end
And if you really want to keep it dry use inheritance to setup your test classes:
class BaseIntegrationTest < ActionDispatch::IntegrationTest
include Devise::Test::ControllerHelpers
include Warden::Test::Helpers
include SessionHelper
end
class TravelsIntegrationTest < BaseIntegrationTest
test 'should accept correct fields' do
login
# ...
end
end