Сессии не сохраняются в интеграционных тестах Rails 5.2

У меня есть многопользовательское приложение, которое использует 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 требуется аутентифицированный пользователь, поэтому я знаю, что помощник входа работает. Однако всякий раз, когда я пытаюсь перейти по другой ссылке, пользователь внезапно не аутентифицируется.

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

def authenticate_user!(*args)
  byebug
  super
end

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

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

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

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

Система

  • Рельсы: 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

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

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.

вставьте в контроллер статей. некоторые идеи: (i) добавить <% byebug %> в представление articles_path, а затем посмотреть, есть ли у вас текущий пользователь - и если пользователь вошел в систему? (ii) проверьте, было ли выполнено действие new в контроллере статей, снова используя byebug, и снова проверьте, есть ли у вас там current_user. Затем пройдитесь, строка за строкой, проверяя, есть ли у вас еще текущий пользователь. Надеюсь, это поможет.

BenKoshy 03.02.2019 11:52

Спасибо @BKSpurgeon, я обновил вопрос с помощью ArticlesController. При вставке <% byebug %> в представление индекса я обнаружил, что пользователь является вошел в систему. Однако он никогда не доходит до действия new, потому что он не выполняет before_actionauthenticate_user! Devise. Вот почему я предположил, что это как-то связано с сеансами, которые не сохраняются в тестовой среде.

Will 04.02.2019 20:59

Попробуйте удалить патч для обезьян и в своем помощнике по входу добавить оператор bybug и проверить, успешно ли пользователь вошел в систему?

BenKoshy 05.02.2019 04:29

Я не уверен, как я буду проверять, вошел ли пользователь в систему без патча для обезьян. В тесте интеграции у меня нет доступа к current_user, warden или env.

Will 05.02.2019 05:12

см. здесь: gist.github.com/BKSpurgeon/56cdf362d78848b070daf1bd5bd3ba2e

BenKoshy 05.02.2019 05:29

Спасибо за пример @BKSpurgeon. Я удалил патч обезьяны и вставил byebug, но я не уверен, как я буду проверять, вошел ли пользователь в систему. Обычно я проверял бы это с помощью current_user, warden или env, но они недоступны в контексте ActionDispatch::IntegrationTest

Will 05.02.2019 05:34

я обновил суть с дальнейшими комментариями. важно - удалить патч обезьяны, прежде чем тестировать эти вещи. возможно, в новой ветке git.

BenKoshy 05.02.2019 08:47

Давайте продолжить обсуждение в чате.

Will 05.02.2019 17:18

Я предполагаю, что что-то не так с вашей настройкой, из-за которой файл cookie аутентификации, установленный через вход в систему, не отправляется (или не принимается) в будущих запросах. Очень сложно отлаживать без рабочего примера. Взгляните на github.com/randoum/as_bug, который Бендж создал для этот вопрос, в качестве примера/отправной точки для создания минимального примера для отладки проблем с RSpec на Rails и создайте что-то подобное, чтобы мы могли в нем разобраться.

Old Pro 09.02.2019 04:33
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
9
1 109
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Хорошо, я наткнулся на ответ после того, как @БКСперджен (в сообщении чата) и @Старый профессионал рекомендовали мне попытаться воссоздать проблему в отдельном приложении, чтобы поделиться со всеми вами.

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

# 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

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

рад, что вы решили это. Ваш комментарий: «Меня все еще смущает одна вещь: почему я вообще смог войти в систему? Я списал это на затянувшиеся тестовые данные, но это скорее предположение, чем что-либо еще»....... ....не гадай: попробуй выяснить, почему. если это НЕ устаревшие тестовые данные, то у вас будет серьезная брешь в системе безопасности!!

BenKoshy 27.01.2020 05:02

Другие вопросы по теме