问题
I've been following M. Hartl's Rails Tutorial and I've encountered a problem. As I moved from chapter 10 all by code was working and my test suite was passing. In chapter 11, all I have done so far is to create a new branch. None of my files have been altered. Yet somehow, my test suite is now failing. Even more strange, is that it doesn't fail 100% of the time. Sometimes it passes, but most of the time it fails (if I run it again and again without changing any code). The relevant code and error message is below. Any help would be appreciated very much. Thanks
Error:
FAIL["test_valid_signup_information_with_account_activation", UsersSignupTest, 3.902866843]
test_valid_signup_information_with_account_activation#UsersSignupTest (3.90s)
Expected: 1
Actual: 2
test/integration/users_signup_test.rb:29:in `block in <class:UsersSignupTest>'
user_signup_test.rb
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, user: {name: "Stevan",
email: "foo@invalid",
password: "foo",
password_confirmation: "bar"}
end
assert_template 'users/new'
assert_select 'div#error_explanation'
assert_select 'div.alert-danger'
end
test "valid signup information with account activation" do
get signup_path
#name = "Example User"
#email = "user@example.com"
#password = "password"
assert_difference 'User.count', 1 do
post users_path, user:{name: "Example User",
email: "user@example.com",
password: "password",
password_confirmation: "password"}
end
assert_equal 1, ActionMailer::Base.deliveries.size
user = assigns(:user)
assert_not user.activated?
# Try to log in before activation.
log_in_as(user)
assert_not is_logged_in?
# Invalid activation token
get edit_account_activation_path("invalid token")
assert_not is_logged_in?
# Valid token, wrong email
get edit_account_activation_path(user.activation_token, email: 'wrong')
assert_not is_logged_in?
# Valid activation token
get edit_account_activation_path(user.activation_token, email: user.email)
assert user.reload.activated?
follow_redirect!
assert_template 'users/show'
assert is_logged_in?
end
end
users_controller.rb
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def index
@users = User.paginate(page: params[:page])
end
def new
@user = User.new
end
def show
@user = User.find(params[:id])
end
def create
@user = User.new(user_params)
if @user.save
#UserMailer.account_activation(@user).deliver #deliver_now
@user.send_activation_email
flash[:info] = "Please check your email to activate your account"
redirect_to root_url
else
render 'new'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
def edit
#@user = User.find(params[:id]) no longer needed as we have implemented correct_user
end
def update
#@user = User.find(params[:id]) no longer needed as we have implemented correct_user
if @user.update_attributes(user_params)
flash[:success] = 'Profile updated'
redirect_to @user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation) #haven't permit admin -> not editable in the browser
end
#before filters
#confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
#confirms the currect user is the user currently logged in
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user) #@user==current_user refactored
end
#confirms as admin user
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
user.rb
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates(:name, presence:true , length: {maximum: 50})
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]{2,4}+\z/i
validates(:email, presence:true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false})
has_secure_password
validates :password, length: {minimum: 6}, allow_blank: true
#returns the hash digest of the given string
def User.digest(string) #more idimatically correct to define as self.digest
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
#returns a random token
def User.new_token #more idiomatically correct to define as self.new_token
SecureRandom.urlsafe_base64
end
#remembers a user in the database for use in persistent sessions
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
#forgets a user
def forget
update_attribute(:remember_digest, nil)
end
#activates an account
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
#send activation email
def send_activation_email
UserMailer.account_activation(self).deliver
end
#sets the password reset attrobutes
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
end
#Returns true if password has expired
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
private
#converts email all to lower case
def downcase_email
self.email = email.downcase
end
#creates and assigns activation token and digest
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
Gemfile
source 'https://rubygems.org'
ruby '2.1.1'
gem 'rails', '4.2.0.beta1'
gem 'bcrypt', '3.1.7'
gem 'faker', '1.4.2'
gem 'will_paginate', '3.0.7'
gem 'bootstrap-will_paginate','0.0.10'
gem 'byebug', '3.5.1'
gem 'bootstrap-sass', '3.2.0.0'
gem 'sass-rails', '5.0.0.beta1'
gem 'uglifier', '2.5.1'
gem 'coffee-rails', '4.0.1'
gem 'jquery-rails', '3.1.1'
gem 'turbolinks', '2.3.0'
gem 'jbuilder', '2.1.3'
gem 'sdoc', '0.4.0', group: :doc
group :test do
gem 'minitest-reporters', '1.0.5'
gem 'mini_backtrace', '0.1.3'
gem 'guard-minitest', '2.3.1'
end
group :development do
gem 'sqlite3', '1.3.9'
gem 'spring', '1.1.3'
end
group :production do
gem 'pg', '0.17.1'
gem 'rails_12factor', '0.0.2'
gem 'unicorn', '4.8.3'
end
回答1:
Your code is missing the setup
method defined in Listing 10.29.
回答2:
Try to add the following before hook to all specs dealing with ActionMailer
before { ActionMailer::Base.deliveries = [] }
It looks like other specs also send emails. Results vary because of arbitrary order of specs.
来源:https://stackoverflow.com/questions/27395083/user-signup-test-failing-in-m-hartls-rails-tutorial