Rails 6, тест системы minitest: сброс пароля не выполняется в тестах, но работает, когда я изменяю его вручную - PullRequest
2 голосов
/ 12 января 2020

Я использую Rails 6 и minitest со встроенными системными тестами (которые используют Capybara, я думаю) и также использую FactoryBot для генерации моих тестовых записей.

У меня довольно стандартная функция восстановления пароля Я пытаюсь реализовать.

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

Это похоже на то, что объект @user кэшируется в тестах и ​​не будет reload в тесте, но я понятия не имею, почему это будет.

Кто-нибудь знает, почему этот тест не пройден, но функциональность работает в «реальной жизни», когда я вручную изменяю пароль?

# test/system/password_resets_test.rb
require "application_system_test_case"

class PasswordResetsTest < ApplicationSystemTestCase
  test "change password" do
    original_password = "password"
    new_password = "new-password"
    @user = create(:user, password: original_password, password_reset_token_sent_at: Time.current)

    visit password_reset_path(@user.password_reset_token)
    fill_in "user[password]", with: new_password
    click_on "Update Password"

    assert_equal(@user.reload.password, new_password)
  end
end
# app/views/password_resets/show.html.erb
<%= form_with model: @user, url: password_reset_path(@user.password_reset_token), method: :put do |form| %>
  <div class="field">
    <%= form.label :password, "Password" %><br />
    <%= form.password_field :password, autofocus: true, required: true %>
  </div>
  <div class="field">
    <%= form.submit "Update Password" %>
  </div>
<% end %>
# app/controllers/password_resets_controller.rb
class PasswordResetsController < ApplicationController
  def show
    if @user = User.find_by(password_reset_token: params[:id])
      if @user.password_reset_token_expired?
        flash[:error] = "Your password reset has expired"
        redirect_to new_password_reset_path
      end
    else
      flash[:error] = "Invalid password reset token"
      redirect_to new_password_reset_path
    end
  end

  def update
    @user = User.find_by(password_reset_token: params[:id])
    new_password = password_reset_params[:password]

    # Automatically set `#password_confirmation` so user does not have
    # to enter in password twice on reset page.
    if @user&.update(password: new_password, password_confirmation: new_password)
      let_user_in(@user)
    else
      render :show
    end
  end

  private

  def password_reset_params
    params.require(:user).permit(:password)
  end
# app/models/user.rb
class User < ApplicationRecord
  PASSWORD_RESET_TIME_LIMIT_IN_HOURS = 4.freeze

  has_secure_password
  has_secure_token :password_reset_token

  validates :password,
    presence: true,
    length: { minimum: 8 },
    allow_nil: true

  def password_reset_token_expired?
    return true if password_reset_token_sent_at.blank?
    password_reset_token_sent_at < PASSWORD_RESET_TIME_LIMIT_IN_HOURS.hours.ago
  end
end

1 Ответ

1 голос
/ 13 января 2020

click_on не гарантирует каких-либо действий, вызванных кликом, произошедшим при его возврате. Это потому, что у Капибары нет возможности узнать, какие действия (если они есть) были бы вызваны этим щелчком. Это означает, что ваше утверждение нового пароля, вероятно, происходит еще до того, как страница была отправлена. Чтобы исправить это, вам нужно использовать одно из представленных в Capybara повторных утверждений (а assert_equal нет), чтобы проверить наличие чего-либо видимого на странице, которое указывает, что произошло обновление.

Что-то вроде

click_on "Update Password"
assert_text "Password Updated!" # whatever message your page shows to indicate successful password update

assert_equal(@user.reload.password, new_password)

должен исправить вашу проблему.

...