M.Hartl 的 Ruby on Rails 教程(第 3 版),第 10 章 (10.54) 中的密码重置测试失败 [英] Password Reset Test failing in M.Hartl's Ruby on Rails Tutorial (3rd edition), Chapter 10 (10.54)

查看:39
本文介绍了M.Hartl 的 Ruby on Rails 教程(第 3 版),第 10 章 (10.54) 中的密码重置测试失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

到目前为止,我的所有测试/断言都已按预期通过.我非常有信心应用程序本身运行良好,但我在这个测试中没有通过一个断言.我是一个 Rails 新手,但我从其他编程经验中知道,现在不解决这个问题很可能会让小鬼恶化.

出于对 SO 成员时间的尊重(并认识到我的 Rails 新手状态),在提出这个问题之前,我已经做了几乎所有人类可能的故障排除,包括:

  1. 重新启动了我的本地 Rails 服务器(多次).
  2. 查看此处有关 Rails 教程(及其他版本)中测试失败的所有其他问题.
  3. 深入研究 Minitest 文档以了解我遇到的错误.
  4. 用@Mhartl 的 Github 存储库中的代码替换了我的(密码重置)集成测试代码.
  5. 在我的测试中尝试了Rails.logger.debug"消息以通过日志消息进行调试.

断言失败消息:

FAIL["test_password_resets", PasswordResetsTest, 2015-07-30 13:42:42 -0400] test_password_resets#PasswordResetsTest (1438278162.33s)断言失败,未给出任何消息.test/integration/password_resets_test.rb:57:in `block in <class:PasswordResetsTest>'

我的 password_resets_test.rb(全部):

需要'test_helper'类 PasswordResetsTest 

第 57 行(失败的断言)是:

assert is_logged_in?

我的 test_helper.rb 的相关部分:

ENV['RAILS_ENV'] ||= 'test'# 为简洁起见编辑...# 如果测试用户已登录,则返回 true.def is_logged_in?!session[:user_id].nil?结尾# 以测试用户登录.def log_in_as(user, options = {})密码 = 选项[:密码] ||'密码'remember_me = options[:remember_me] ||'1'如果集成_测试?post login_path, session: { email: user.email,密码:密码,记住_我:记住_我}别的session[:user_id] = user.id结尾结尾私人的# 在集成测试中返回 true.定义集成_测试?定义?(post_via_redirect)结尾结尾

这是我的 password_resets_controller.rb:

class PasswordResetsController <应用控制器before_action :get_user, only: [:edit, :update]before_action :valid_user, only: [:edit, :update]before_action :check_expiration, only: [:edit, :update] # Listing 10.52定义创建@user = User.find_by(email: params[:password_reset][:email].downcase)如果@user@user.create_reset_digest@user.send_password_reset_emailflash[:info] = "带有密码重置说明的电子邮件发送"重定向到 root_url别的flash.now[:danger] = "未找到电子邮件地址"呈现新"结尾结尾定义更新如果 params[:user][:password].empty?flash.now[:danger] = "密码不能为空"渲染编辑"elsif @user.update_attributes(user_params)登录@用户flash[:success] = "密码已重置."重定向到@user别的渲染编辑"结尾结尾私人的定义用户参数params.require(:user).permit(:password,:password_confirmation)结尾# 过滤前:def get_user@user = User.find_by(email: params[:email])结尾# 确认有效用户.def valid_user除非 (@user && @user.activated? &&@user.authenticated?(:reset, params[:id]))重定向到 root_url结尾结尾# 检查重置令牌的到期时间.def check_expiration如果@user.password_reset_expired?flash[:danger] = "密码重置已过期."redirect_to new_password_reset_url结尾结尾结尾

这是我的 user.rb(已编辑):

class User 

我的 Gemfile(已编辑):

source 'https://rubygems.org'红宝石'2.2.2'宝石导轨",4.2.2"# 为简洁起见编辑...组:开发,:测试做宝石'sqlite3','1.3.9'宝石'再见','3.4.0'宝石网络控制台",2.0.0.beta3"宝石春天",1.1.3"结尾组:测试做gem 'minitest-reporters', '1.0.5'宝石mini_backtrace",0.1.3"gem 'guard-minitest', '2.3.1'结尾# 为简洁起见编辑...

我从事软件开发已有很长时间了,这听起来像是一个经典案例,即绕着轴试图找到一个微妙的问题,而忽略了一些明显的问题.我当然知道我花在这上面的时间比明智的要多,而且我希望在这个过程中我在我的代码中注入了一些废话.

预先感谢您的帮助.

解决方案

我认为这是一个非常简单的错误:

在您的测试中,您在第 53 行提交密码重置表单以为用户选择新密码,但您选择的新密码(foobaz")只有 6 个字符长:

patch password_reset_path(user.reset_token),电子邮件:user.email,用户:{ 密码:foobaz",密码确认:foobaz"}

但是在 user.rb 中你规定密码必须至少为 8 个字符:

验证:密码,存在:真,长度:{最小值:8},allow_nil:真

这就是密码重置失败的原因.使用更长的密码,你应该没问题!

要弄清楚这一点,您可以在失败的断言之前添加这一行:

放入 html_document

哪个会将呈现的 HTML 转储到您的终端窗口,您会在那里找到...

该表单包含 1 个错误.

<ul><li>密码太短(最少8个字符)</li>

All my tests/assertions have passed as expected up until now. I'm pretty confident that the application itself is working fine, but I'm failing one assertion in this test. I'm a Rails rookie, but I know from other programming experiences that not resolving this now is likely leaving a gremlin to fester.

Out of respect for SO members' time (and recognizing my Rails-novice state) I have done just about everything humanly possible to troubleshoot before asking this question, including:

  1. Restarted my local Rails server (multiple times).
  2. Looked at every other question here about tests failing in the Rails Tutorial (and beyond).
  3. Dug into the Minitest documentation to understand the error I'm getting.
  4. Replaced my (password resets) integration test code with code from @Mhartl's Github repo.
  5. Tried "Rails.logger.debug" messages in my test to debug via log messages.

Assertion failure message:

FAIL["test_password_resets", PasswordResetsTest, 2015-07-30 13:42:42 -0400] test_password_resets#PasswordResetsTest (1438278162.33s)
Failed assertion, no message given.
    test/integration/password_resets_test.rb:57:in `block in <class:PasswordResetsTest>'

My password_resets_test.rb (in its entirety):

require 'test_helper'

class PasswordResetsTest < ActionDispatch::IntegrationTest
  def setup
    ActionMailer::Base.deliveries.clear
    @user = users(:michael)
  end

  test "password resets" do
    get new_password_reset_path
    assert_template 'password_resets/new'
    # Invalid email
    post password_resets_path, password_reset: { email: "" }
    assert_not flash.empty?
    assert_template 'password_resets/new'
    # Valid email
    post password_resets_path, password_reset: { email: @user.email }
    assert_not_equal @user.reset_digest, @user.reload.reset_digest
    assert_equal 1, ActionMailer::Base.deliveries.size
    assert_not flash.empty?
    assert_redirected_to root_url
    # Password reset form
    user = assigns(:user)
    # Wrong email
    get edit_password_reset_path(user.reset_token, email: "")
    assert_redirected_to root_url
    # Inactive user
    user.toggle!(:activated)
    get edit_password_reset_path(user.reset_token, email: user.email)
    assert_redirected_to root_url
    user.toggle!(:activated)
    # Right email, wrong token
    get edit_password_reset_path('wrong token', email: user.email)
    assert_redirected_to root_url
    # Right email, right token
    get edit_password_reset_path(user.reset_token, email: user.email)
    assert_template 'password_resets/edit'
    assert_select "input[name=email][type=hidden][value=?]", user.email
    # Invalid password & confirmation
    patch password_reset_path(user.reset_token),
          email: user.email,
          user: { password:              "foobaz",
                  password_confirmation: "barquux" }
    assert_select 'div#error_explanation'
    # Empty password
    patch password_reset_path(user.reset_token),
          email: user.email,
          user: { password:              "",
                  password_confirmation: "" }
    assert_not flash.empty?
    # Valid password & confirmation
    patch password_reset_path(user.reset_token),
          email: user.email,
          user: { password:              "foobaz",
                  password_confirmation: "foobaz" }
    assert is_logged_in? #<=== FAILING ASSERTION
    assert_not flash.empty?
    assert_redirected_to user
  end
end

Line 57 (the failing assertion) is:

assert is_logged_in?

Relevant parts of my test_helper.rb:

ENV['RAILS_ENV'] ||= 'test'

  # Edited for brevity ...

  # Returns true if a test user is logged in.
  def is_logged_in?
    !session[:user_id].nil?
  end

  # Logs in a test user.
  def log_in_as(user, options = {})
    password    = options[:password]    || 'password'
    remember_me = options[:remember_me] || '1'
    if integration_test?
      post login_path, session: { email:       user.email,
                                  password:    password,
                                  remember_me: remember_me }
    else
      session[:user_id] = user.id
    end
  end

  private
    # Returns true inside an integration test.
    def integration_test?
      defined?(post_via_redirect)
    end
end

Here's my password_resets_controller.rb:

class PasswordResetsController < ApplicationController
  before_action :get_user,   only: [:edit, :update]
  before_action :valid_user, only: [:edit, :update]
  before_action :check_expiration, only: [:edit, :update] # Listing 10.52

  def create
    @user = User.find_by(email: params[:password_reset][:email].downcase)
    if @user
      @user.create_reset_digest
      @user.send_password_reset_email
      flash[:info] = "Email sent with password reset instructions"
      redirect_to root_url
    else
      flash.now[:danger] = "Email address not found"
      render 'new'
    end
  end

  def update
    if params[:user][:password].empty? 
      flash.now[:danger] = "Password can't be empty"
      render 'edit'
    elsif @user.update_attributes(user_params)
      log_in @user
      flash[:success] = "Password has been reset."
      redirect_to @user
    else
      render 'edit'
    end
  end

  private

    def user_params
      params.require(:user).permit(:password, :password_confirmation)
    end

    # Before filters:

    def get_user
      @user = User.find_by(email: params[:email])
    end

    # Confirms a valid user.
    def valid_user
      unless (@user && @user.activated? &&
              @user.authenticated?(:reset, params[:id]))
        redirect_to root_url
      end
    end

    # Checks expiration of reset token.
    def check_expiration
      if @user.password_reset_expired?
        flash[:danger] = "Password reset has expired."
        redirect_to new_password_reset_url
      end
    end
end

Here's my user.rb (edited):

class User < ActiveRecord::Base
  # Add tokens to class accessor:
  attr_accessor :remember_token, :activation_token, :reset_token

  # Edited for brevity ...

  # Returns true if the given token matches the digest.
  def authenticated?(attribute, token)
    digest = send("#{attribute}_digest")
    return false if digest.nil? # ... implied else here ...
    BCrypt::Password.new(digest).is_password?(token)
  end

  # Edited for brevity ...

  # Sets the password reset attributes.
  def create_reset_digest
    self.reset_token = User.new_token
    update_attribute(:reset_digest,  User.digest(reset_token))
    update_attribute(:reset_sent_at, Time.zone.now)
  end

  # Sends password reset email.
  def send_password_reset_email
    UserMailer.password_reset(self).deliver_now
  end

  # Returns true if a password reset has expired.
  def password_reset_expired?
    reset_sent_at < 2.hours.ago
  end

 # Edited for brevity ...

end

My Gemfile (edited):

source 'https://rubygems.org'
ruby '2.2.2'
gem 'rails',        '4.2.2'

# Edited for brevity ...

group :development, :test do
  gem 'sqlite3',     '1.3.9'
  gem 'byebug',      '3.4.0'
  gem 'web-console', '2.0.0.beta3'
  gem 'spring',      '1.1.3'
end

group :test do
  gem 'minitest-reporters', '1.0.5'
  gem 'mini_backtrace',     '0.1.3'
  gem 'guard-minitest',     '2.3.1'
end

# Edited for brevity ...

I've been around software development for a long time, and this smells like a classic case of getting wrapped around the axle trying to find a subtle problem while overlooking something obvious. I definitely know I spent more time on this than is sensible, and I expect I have injected some nonsense in my code during that process.

Thanks in advance for your help.

解决方案

I think this is a pretty simple mistake:

In your test, on line 53 you are submitting the password reset form to choose a new password for the user, but the new password you've chosen ("foobaz") is only 6 characters long:

patch password_reset_path(user.reset_token),
      email: user.email,
      user: { password:              "foobaz",
              password_confirmation: "foobaz" }

But then in user.rb you stipulate that passwords must be at least 8 characters:

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

So that's why password reset fails. Use a longer password and you should be OK!

To figure this out, you could have added this line just before the failing assertion:

puts html_document

Which would dump the rendered HTML to your terminal window, where you would find...

<div class="alert alert-danger">
  The form contains 1 error.
</div>
<ul>
  <li>Password is too short (minimum is 8 characters)</li>
</ul>

这篇关于M.Hartl 的 Ruby on Rails 教程(第 3 版),第 10 章 (10.54) 中的密码重置测试失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆