如何在 Rails 中进行 DRY 测试并仍然使用 Capybara 和 Warden? (使用最小测试)
How can I make tests DRY in Rails and still use Capybara and Warden? (using minitest)
我的目的是为同一个表格创建几个测试,在这些测试中我需要 Capybara 来完整填写表格。不过,我还是想避免重写填写表格的代码。
我没有使用 RSPEC!我正在使用 minitest.
我面临的问题是无法从我的 CapybaraHelper
模块访问 Capybara 方法 visit
和 Warden 辅助方法 login_as
。
我看到了这个问题,所以我在 test/support
文件夹中创建了我的模块,但是我提到的方法仍然无法访问。
How to reuse code in Capybara
我看过这个问题,但我想重用的代码似乎不适合 setup
方法和 teardown
方法。
Rails: Premake a model instance for your unit tests (for DRY purposes)?
我也看到帖子说这个模块应该在 test_helper.rb
里面,但是当我向这个文件添加更多模块时它会变得混乱。
所以现在我想知道我做错了什么。我试过将以下行添加到 CapybaraHelper
但它没有帮助。它实际上引发了错误 NoMethodError: undefined method
setup' for CapybaraHelper:Module`.
include Devise::Test::ControllerHelpers
include Warden::Test::Helpers
在测试中重用代码是否正确?我是否遗漏了一些应该包含在我的帮助模块中的东西?所有这些方法在使用 CapybaraHelper:Module
.
的测试控制器中都能完美运行
错误信息如下:
NoMethodError: undefined method `login_as' for CapybaraHelper:Module
这里是使用 CapybaraHelper:Module
.
的另一个测试的错误消息
NoMethodError: undefined method `visit' for CapybaraHelper:Module
这是我的测试:
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
这是我在 test/support/capybara/capybara_helper.rb
中为避免代码重复而创建的助手:
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
您应该使用 ActionDispatch::IntegrationTest
而不是 ActionController::TestCase
作为父 class 来使用水豚进行测试。 ActionController::TestCase
模拟了请求阶段和 Rails 的大部分内容。它在 Rails 5 中贬值。
与其调用测试辅助模块上的方法,不如将它们混合到测试中 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
除此之外,您缺乏代码组织 - 设置 Warden.test_mode!
等设置步骤应该在您的 test_helper.rb
中完成,而不是在您的测试中重复。也不要将所有步骤定义都放入一个文件中。例如,您可以将它们放在 /test/support/
中。
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
如果你真的想保留它,请使用继承来设置你的测试 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
我的目的是为同一个表格创建几个测试,在这些测试中我需要 Capybara 来完整填写表格。不过,我还是想避免重写填写表格的代码。
我没有使用 RSPEC!我正在使用 minitest.
我面临的问题是无法从我的 CapybaraHelper
模块访问 Capybara 方法 visit
和 Warden 辅助方法 login_as
。
我看到了这个问题,所以我在 test/support
文件夹中创建了我的模块,但是我提到的方法仍然无法访问。
How to reuse code in Capybara
我看过这个问题,但我想重用的代码似乎不适合 setup
方法和 teardown
方法。
Rails: Premake a model instance for your unit tests (for DRY purposes)?
我也看到帖子说这个模块应该在 test_helper.rb
里面,但是当我向这个文件添加更多模块时它会变得混乱。
所以现在我想知道我做错了什么。我试过将以下行添加到 CapybaraHelper
但它没有帮助。它实际上引发了错误 NoMethodError: undefined method
setup' for CapybaraHelper:Module`.
include Devise::Test::ControllerHelpers
include Warden::Test::Helpers
在测试中重用代码是否正确?我是否遗漏了一些应该包含在我的帮助模块中的东西?所有这些方法在使用 CapybaraHelper:Module
.
错误信息如下:
NoMethodError: undefined method `login_as' for CapybaraHelper:Module
这里是使用 CapybaraHelper:Module
.
NoMethodError: undefined method `visit' for CapybaraHelper:Module
这是我的测试:
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
这是我在 test/support/capybara/capybara_helper.rb
中为避免代码重复而创建的助手:
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
您应该使用 ActionDispatch::IntegrationTest
而不是 ActionController::TestCase
作为父 class 来使用水豚进行测试。 ActionController::TestCase
模拟了请求阶段和 Rails 的大部分内容。它在 Rails 5 中贬值。
与其调用测试辅助模块上的方法,不如将它们混合到测试中 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
除此之外,您缺乏代码组织 - 设置 Warden.test_mode!
等设置步骤应该在您的 test_helper.rb
中完成,而不是在您的测试中重复。也不要将所有步骤定义都放入一个文件中。例如,您可以将它们放在 /test/support/
中。
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
如果你真的想保留它,请使用继承来设置你的测试 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