Тест Rails — проверка, вошел ли пользователь в Devise

Я пытаюсь проверить, что кто-то может войти на мой сайт, отправив запрос POST на мой SessionsController. Я видел, как этот способ рекомендуется в нескольких местах:

it 'must be able to sign in a user' do
  user = create(:user)
  post :create, format: :js, user: {email: user.email, password: user.password, remember_me: 0}
  assert_response :success
  @controller.current_user.must_equal user
end

Но этот тест неверен. Вызов @controller.current_user попытается аутентифицировать пользователя с использованием опубликованных параметров и вернет user, если указанный адрес электронной почты/пароль верен. Нет никакой гарантии, что действие create на самом деле вызывает sign_in или current_user.

Даже если я перепишу тест, чтобы проверить, вызываются ли эти методы, возможно, что другие методы могут быть вызваны, например. sign_out.

Есть ли более точный способ окончательно проверить, вошел ли пользователь в систему, и если да, то кто этот пользователь?

РЕДАКТИРОВАТЬ -

Например, следующий тест пройдет

it 'must sign in a user' do
   @controller.current_user.must_equal nil
   post :create, format: :js, user: {email: @user.email, password: @user.password, remember_me: 0}
   assert_response :success
   @controller.current_user.must_equal @user
end

когда действие SessionsController#create:

def create
  respond_to do |format|
      format.js {
        render nothing: true, status: 200
      }
  end
end

person user1063998    schedule 24.08.2016    source источник
comment
Может ли это помочь? stackoverflow.com/questions/23793597/   -  person tmn4jq    schedule 24.08.2016
comment
Боюсь, я не думаю, что это поможет. У меня нет проблем с вызовом current_user.   -  person user1063998    schedule 26.08.2016
comment
является ли .current_user методом, который пытается войти в систему, или это attr_reader для переменной экземпляра? Если первое, то кажется, что вы описываете ожидаемое поведение.   -  person max pleaner    schedule 05.09.2016
comment
current_user - это метод, который попытается войти в систему. Если вы запустите тест, который я описал в вопросе, с полностью пустым действием контроллера, тест все равно пройдет   -  person user1063998    schedule 08.09.2016
comment
Как вы обрабатываете сеансы в контроллере? вы используете рельсы session?   -  person Narasimha Reddy - Geeker    schedule 08.09.2016


Ответы (1)


Решение с минимальными изменениями в предлагаемом коде в вопросе:

Перед запуском теста необходимо инициализировать систему. Попробуйте добавить следующий код перед кодом it 'must be able to sign in a user' do:

before (:each) do
  user = FactoryGirl.create(:user)
  sign_out user
end

Это должно превратить ваш тест в действительный тест для вашего пост-контроллера.

Пояснение:

Я предполагаю, что ваш тест выше всегда завершается успешно, потому что пользователь уже вошел в систему (другими тестами, запущенными до этого). Вы можете проверить это, используя byebug в строке после it и запустив current_user в консоли bybug's. Если это не nil, пользователь уже вошел в систему, что делает ваш тест недействительным.

Обратите внимание, что (в отличие от того, что обсуждалось выше в комментариях), current_user не меняет статус пользователя; это функция только для чтения.

Короткое и чистое решение:

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

def sign_in_via_post(user)
  post :create, format: :js, user: {email: user.email, password: user.password, remember_me: 0}
end

...

before (:each) do
  user = FactoryGirl.create(:user)
  sign_out user
end

it 'must be able to sign in a user' do
   { sign_in_via_post user }.should change { current_user }.from(nil).to(user)
end

С помощью оператора should change from nil to user вы проверяете, что пользователь вышел из системы до начала теста и что пользователь вошел в систему после того, как тест был выполнен.

Обратите внимание, что часть

   { sign_in_via_post user }.should change { current_user }.from(nil).to(user)

эквивалентен (возможно, более простому для понимания) коду

   { sign_in_via_post user }.should change { user_signed_in? }.from(false).to(true)

как обсуждалось здесь.

person Olli    schedule 08.09.2016
comment
Спасибо за ответ. Я верю, что current_user может обновлять состояние пользователя. Я добавил образец теста в свой исходный вопрос, который, как мне кажется, иллюстрирует проблему. - person user1063998; 09.09.2016
comment
У меня просто возникла мысль, может быть, есть фильтр before_action/filter, который что-то делает, но я не могу проверить прямо сейчас. Возможно, вы правы - person user1063998; 09.09.2016
comment
@ user1063998: у вас была возможность проверить мое предложение? Я потратил довольно много времени, чтобы найти решение (по крайней мере, я думаю, что это решение). В любом случае, это было хорошее упражнение, но я бы предпочел, чтобы оно было проверено тем, кто спрашивал. - person Olli; 12.09.2016