Сессии не сохраняются в интеграционных тестах Rails 5.2 - PullRequest
0 голосов
/ 03 февраля 2019

У меня есть мультитенантное приложение, которое использует Apartment для схем postgreSQL и Devise для аутентификации пользователей.Все идет гладко, пока я не попытаюсь написать некоторые интеграционные тесты.

Вот урезанная версия того, что у меня есть (не стесняйтесь спрашивать дополнительную информацию):

# test/support/sign_in_helper.rb
module SignInHelper
  def sign_in_as(name)
    sign_in users(name)
  end
end


# test/test_helper.rb
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }

#...

class ActionDispatch::IntegrationTest
  include Devise::Test::IntegrationHelpers
  include SignInHelper
end


# test/system/article/post_test.rb
require "application_system_test_case"

class Article::PostTest < ApplicationSystemTestCase
  test 'post a new document' do
    sign_in_as :will
    visit articles_path

    click_on 'Add New Article' # redirected to login page after clicking this button
    fill_in 'Name', with: 'Hello world!'
    fill_in 'Content', with: 'Yes yes'

    click_on 'Create Article'
    assert_select 'h1', /Hello world!/
  end
end

articles_path требует аутентифицированного пользователя, так что я знаю, что вход в помощник работает.Тем не менее всякий раз, когда я пытаюсь перейти по другой ссылке, внезапно пользователь не проходит проверку подлинности.

Я обезьяна исправил метод authenticate_user! Devise следующим образом:

def authenticate_user!(*args)
  byebug
  super
end

и подтвердил, что warden.authenticated? вернул true для articles_path, но false при последующей попытке перейти к new_article_path.

Я заметил это поведение как в типах интеграционных тестов, так и в контроллере и системе.Тем не менее, это не проблема при использовании этого приложения в среде разработки.

Наиболее расстраивающим моментом является то, что у меня есть другое приложение, которое, похоже, имеет настройку, идентичную этому приложению, но эта проблема аутентификации делаетне происходит во время тестирования.

Как можно отладить эту проблему?

Система

  • Rails: 5.2.2
  • Разработка: 4.5.0
  • Капибара: 3.13.2

Обновление 1 (04 февраля 2019 г.)

Вот контроллер статей, запрошенный @ BKSpurgeon

# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  before_action :set_article, only: [:show, :edit, :update, :archive]

  def index
    @articles = Article.where(archived: false)
  end

  def show
  end

  def new
    @article = Article.new
  end

  def create
    @article = Article.new(article_params)

    if @article.save
      redirect_to @article, notice: 'Your article was successfully created.'
    else
      flash[:error] = @article.errors.full_messages.to_sentence
      render :new
    end
  end

  def edit
  end

  def update
    if @article.update(article_params)
      redirect_to articles_path, notice: 'Your article was successfully updated.'
    else
      flash[:error] = @article.errors.full_messages.to_sentence
      render :edit
    end
  end

  def archive
    if @article.archive!
      redirect_to articles_path, notice: 'Your article was successfully archived.'
    else
      render :edit
    end
  end

  private
    def set_article
      @article = Article.find(params[:id])
    end

    def article_params
      params.require(:article).permit(:name, :content, :archived)
    end
end

Обновление 2 (04 февраля 2019 г.)

Я написал простое промежуточное ПО и поместил его перед Wardenдля отладки:

# lib/debug_warden_middleware.rb
class DebugWardenMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    @status, @headers, @response = @app.call(env)
    if env['warden'].present?
      puts "User (#{env['warden'].user.present?}), Class: #{@response.class.name}"
    end
    return [@status, @headers, @response]
  end
end

# config/application.rb
#...
module AppName
  class Application < Rails::Application
    # ...

    config.middleware.insert_before Warden::Manager, DebugWardenMiddleware
  end
end

И я заметил, что warden, кажется, очищает своего пользователя после каждого запроса, включая запросы на ресурсы:

bin/rails test:system
Run options: --seed 39763

# Running:

Capybara starting Puma...
* Version 3.9.1 , codename: Private Caller
* Min threads: 0, max threads: 4
* Listening on tcp://127.0.0.1:57466
User (true), Uri: ActionDispatch::Response::RackBody
User (false), Uri: Sprockets::Asset
User (false), Uri: Sprockets::Asset
User (false), Uri: Sprockets::Asset
User (false), Uri: ActionDispatch::Response::RackBody
User (false), Uri: ActionDispatch::Response::RackBody
User (false), Uri: Sprockets::Asset
[Screenshot]: tmp/screenshots/failures_test_post_a_new_article.png
E

Error:
Agenda::PostTest#test_post_a_new_article:
Capybara::ElementNotFound: Unable to find field "Name"
    test/system/article/post_test.rb:9:in `block in <class:PostTest>'


bin/rails test test/system/article/post_test.rb:4

В качестве примечания, я использую оба Sprocketsи Webpacker.

1 Ответ

0 голосов
/ 14 февраля 2019

Хорошо, я наткнулся на ответ после того, как @ BKSpurgeon (в сообщении чата) и @ Old Pro рекомендовал мне попытаться воссоздать проблему в отдельном приложении, чтобы поделиться свы все.

Поскольку это приложение является мультитенантным и использует жемчужину квартиры, мои настройки тестирования были немного запутанными.К сожалению, я не включил некоторые из этих настроек, думая, что это не имеет значения ( большая ошибка, огромная! ).Только когда я попытался воссоздать свое приложение, я заметил недостаток.Вот мои настройки тестирования, которые я использовал:

# test/test_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
require 'rails/test_help'

# support files
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }

# Apartment setup
Apartment::Tenant.drop('sample') rescue nil
Apartment::Tenant.create('sample') rescue nil
Apartment::Tenant.switch! 'sample'

# Capybara setup
Capybara.configure do |config|
  config.default_host = 'http://lvh.me'
  config.always_include_port = true
end

class ActiveSupport::TestCase
  # 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...
end

class ActionDispatch::IntegrationTest
  include Devise::Test::IntegrationHelpers
  include SubdomainHelper
  include SignInHelper # dependent on SubomainHelper

  setup do
    default_url_options[:host] = 'lvh.me'
    Apartment::Tenant.switch! 'public'
  end
end

...

# test/support/sign_in_helper.rb
module SignInHelper
  def sign_in_as(name)
    # This right here was the problem code
    user = users(name)
    use_account user.account
    sign_in user
  end
end

...

# test/support/subdomain_helper.rb
module SubdomainHelper
  def use_account(account)
    account.save unless account.persisted?

    default_url_options[:host] = "#{account.subdomain}.lvh.me"
    Capybara.app_host = "http://#{account.subdomain}.lvh.me"
    Apartment::Tenant.switch! account.subdomain
  end
end

...

# test/system/article/post_test.rb
require "application_system_test_case"

class Article::PostTest < ApplicationSystemTestCase
  test 'post a new document' do
    sign_in_as :will
    visit articles_path

    click_on 'Add New Article' # redirected to login page after clicking this button
    fill_in 'Name', with: 'Hello world!'
    fill_in 'Content', with: 'Yes yes'

    click_on 'Create Article'
    assert_select 'h1', /Hello world!/
  end
end

В итоге проблема заключалась в том, что я пытался захватить пользователя в SignInHelper до того, как фактически переключился на арендатора, содержащего этого пользователя.Обладая этими знаниями, я изменил свой код так, чтобы он выглядел следующим образом:

# test/support/sign_in_helper.rb
module SignInHelper
  def sign_in_as(name, account)
    use_account accounts(account)
    sign_in users(name)
  end
end

...

# test/system/article/post_test.rb
require "application_system_test_case"

class Article::PostTest < ApplicationSystemTestCase
  test 'post a new document' do
    sign_in_as :will, :sample

    # ...
  end
end

И теперь сеансы продолжаются.Одна вещь, которая все еще смущает меня, - почему я вообще мог войти?Я связал это с затяжными данными испытаний, но это скорее предположение, чем все остальное.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...