如何在 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 methodsetup' 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