Integration test (with Test/Unit) of update method:
test "do the patch" do
user = users(:alex)
get signin_url
assert_response :success
post_via_redirect signin_path, email: user.email, password: 'qwerty'
assert_equal profile_path, path
get edit_user_url(user)
patch_via_redirect user_url(user),
email: 'patch#it.man',
name: 'Patch!',
password: 'qwerty',
password_confirmation: 'qwerty'
assert_equal 'User updated!', flash[:notice]
end
When I run the test I got this error:
1) Error:
UserFlowsTest#test_do_the_patch:
ActionController::ParameterMissing: param not found: user
app/controllers/users_controller.rb:43:in `user_params'
app/controllers/users_controller.rb:31:in `update'
test/integration/user_flows_test.rb:124:in `block in <class:UserFlowsTest>'
Functions in my users_controller.rb:
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = t('activecontroller.actions.user.updated')
sign_in #user
redirect_to #user
else
render :edit
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
How to test the patch, when I use strong parameters in my controller?
params.require(:user) requires there to be a :user => param at the root of the parameters hash.
Try this:
patch_via_redirect user_url(user), { user: {
email: 'patch#it.man',
name: 'Patch!',
password: 'qwerty',
password_confirmation: 'qwerty'
} }
Terrible formatting, but should get the point across.
Related
I'm plugging away after an extended leave (a year) on the Ruby on Rails Tutorial by Michael Hartl. I'm getting a user_param error in an integration test. I've gone to 4 sources trying to figure out where I'm going wrong on the strong parameters including the documentations.
Since starting up again last week, I have four hours generally getting to know the tutorial project and another twelve hours reading over chapters 7 to 12, then an hour chasing this problem. I keep feeling like I must be scanning too quickly over this stuff & keenly realizing I am not good at tracing errors.
Can anyone shed some light on what I'm missing? I'd also like any tips on trouble shooting this type of thing too! Thanks for your time!
Here's the error message I'm getting from console:
ERROR["test_password_resets", PasswordResetsTest, 2016-10-20 15:24:37 +0000]
test_password_resets#PasswordResetsTest (1476977077.03s)
NoMethodError: NoMethodError: undefined method `[]' for nil:NilClass
app/controllers/password_resets_controller.rb:10:in `create'
test/integration/password_resets_test.rb:14:in `block in <class:PasswordResetsTest>'
app/controllers/password_resets_controller.rb:10:in `create'
test/integration/password_resets_test.rb:14:in `block in <class:PasswordResetsTest>'
ERROR["test_invalid_signup_information", UsersSignupTest, 2016-10-20 15:24:37 +0000]
test_invalid_signup_information#UsersSignupTest (1476977077.04s)
ActionController::ParameterMissing: ActionController::ParameterMissing: param is missing or the value is empty: user
app/controllers/users_controller.rb:52:in `user_params'
app/controllers/users_controller.rb:25:in `create'
test/integration/user_signup_test.rb:12:in `block (2 levels) in <class:UsersSignupTest>'
test/integration/user_signup_test.rb:11:in `block in <class:UsersSignupTest>'
app/controllers/users_controller.rb:52:in `user_params'
app/controllers/users_controller.rb:25:in `create'
test/integration/user_signup_test.rb:12:in `block (2 levels) in <class:UsersSignupTest>'
test/integration/user_signup_test.rb:11:in `block in <class:UsersSignupTest>'
ERROR["test_valid_signup_information_with_account_activation", UsersSignupTest, 2016-10-20 15:24:37 +0000]
test_valid_signup_information_with_account_activation#UsersSignupTest (1476977077.05s)
ActionController::ParameterMissing: ActionController::ParameterMissing: param is missing or the value is empty: user
app/controllers/users_controller.rb:52:in `user_params'
app/controllers/users_controller.rb:25:in `create'
test/integration/user_signup_test.rb:25:in `block (2 levels) in <class:UsersSignupTest>'
test/integration/user_signup_test.rb:24:in `block in <class:UsersSignupTest>'
app/controllers/users_controller.rb:52:in `user_params'
app/controllers/users_controller.rb:25:in `create'
test/integration/user_signup_test.rb:25:in `block (2 levels) in <class:UsersSignupTest>'
test/integration/user_signup_test.rb:24:in `block in <class:UsersSignupTest>'
FAIL["test_should_get_edit", PasswordResetsControllerTest, 2016-10-20 15:24:37 +0000]
test_should_get_edit#PasswordResetsControllerTest (1476977077.05s)
Expected response to be a <success>, but was <302>
test/controllers/password_resets_controller_test.rb:11:in `block in <class:PasswordResetsControllerTest>'
Here's the controller in question (users_controller):
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 destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
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)
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 correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
Here's the user_signup_test:
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
end
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user#invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
assert_select 'div#error_explanation'
assert_select 'div.field_with_errors'
end
test "valid signup information with account activation" do
get signup_path
assert_difference 'User.count', 1 do
post users_path, params: { 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", email: user.email)
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
Here is the last controller - missed that one somehow ...
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_select 'div#error_explanation'
# Valid password & confirmation
patch password_reset_path(user.reset_token),
email: user.email,
user: { password: "foobaz",
password_confirmation: "foobaz" }
assert is_logged_in?
assert_not flash.empty?
assert_redirected_to user
end
end
PasswordResetController:
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
before_action :check_expiration, only: [:edit, :update] # Case (1)
def new
end
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 edit
end
def update
if params[:user][:password].empty? # Case (3)
#user.errors.add(:password, "can't be empty")
render 'edit'
elsif #user.update_attributes(user_params) # Case (4)
log_in #user
flash[:success] = "Password has been reset."
redirect_to #user
else
render 'edit' # Case (2)
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
I think you should remove params key before user parameters, e.g.:
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, user: { name: "",
email: "user#invalid",
password: "foo",
password_confirmation: "bar" }
end
assert_template 'users/new'
assert_select 'div#error_explanation'
assert_select 'div.field_with_errors'
end
Also you can debug received parameters in controller with pry:
add gem pry to your Gemfile (if there is no one);
run $ bundle install;
add binding.pry in controller before method user_params is called;
check that params contains expected parameters.
I have the following RSpec test defined:
test 'invalid signup information' do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: '',
email: 'user#invalid',
password: 'foo',
password_confirmation: 'bar' } }
end
assert_template 'users/new'
end
And the following Controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to new_user_path
else
render :new
end
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
If I execute rake test I get the following error:
ERROR["test_invalid_signup_information", UserSignupTest, 0.35556070700022246]
test_invalid_signup_information#UserSignupTest (0.36s)
ActionController::ParameterMissing: ActionController::ParameterMissing: param is missing or the value is empty: user
app/controllers/users_controller.rb:23:in `user_params'
app/controllers/users_controller.rb:8:in `create'
test/integration/user_signup_test.rb:7:in `block (2 levels) in <class:UserSignupTest>'
test/integration/user_signup_test.rb:6:in `block in <class:UserSignupTest>'
The test runs without problems if i delete the require statement in user_params. But I do send a user - So why does it fail?
I do not if this is right but in my opinion, you created an integration test but should be a test controller.
See this example
# spec/controllers/contacts_controller_spec.rb
# rest of spec omitted ...
describe "POST create" do
context "with valid attributes" do
it "creates a new contact" do
expect{
post :create, contact: {name: 'test'}
}.to change(Contact,:count).by(1)
end
more info for integration test
I've been working through M.Hartl's Ruby on Rails tutorial for a while now, and I've been able to work out when I've made typos that caused my integration tests to fail and the like, but I've run into one that I can't figure out. My get edit_password_reset_path() with correct arguments is redirecting to the home url, not password_resets/edit like it should.
The failing test:
FAIL["test_password_resets", PasswordResetsTest, 2015-11-02 15:50:02 +0000]
test_password_resets#PasswordResetsTest (1446479402.08s)
expecting <"password_resets/edit"> but rendering with <[]>
test/integration/password_resets_test.rb:37:in `block in <class:PasswordResetsTest>'
Here is my password_resets_test.rb:
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' # This is the assertion that fails
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: "foobazzz",
password_confirmation: "barquuxz" }
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: "foobazzz",
password_confirmation: "foobazzz" }
assert is_logged_in?
assert_not flash.empty?
assert_redirected_to user
end
end
Here is 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]
def new
end
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 edit
end
def update
if params[:user][:password].empty?
#user.errors.add(: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
def get_user
#user = User.find_by(email: params[:email])
end
def valid_user
unless (#user && #user.activated? &&
#user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
def check_expiration
if #user.password_reset_expired?
flash[:danger] = "Password reset has expired."
redirect_to new_password_reset_url
end
end
end
The authenticated? method in user.rb :
# 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
I'm not super sure what else is needed to figure out my problem. I've searched StackOverflow, worked through the code a second time, and I've even compared the source code at M.Hartl's github repo.
Link to the appropriate branch on my repo.
I'm trying to follow exactly this part of the tutorial.
The problem was in the user.rb model.
In my model, I had the following code:
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_digest))
update_attribute(:reset_sent_at, Time.zone.now)
end
Note the second parameter to the first update_attribute() call, reset_digest. That should actually be reset_token. The correct method looks like this:
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
I have run into 4 errors on section 9.2.2.
Errors
ERROR["test_unsuccessful_edit", UsersEditTest, 2015-11-05 04:35:59 -0600]
test_unsuccessful_edit#UsersEditTest (1446719759.23s)
NoMethodError: NoMethodError: undefined method correct_user?' for #<UsersController:0x007fcdf48ad378>
app/controllers/users_controller.rb:58:incorrect_user'
test/integration/users_edit_test.rb:10:in block in <class:UsersEditTest>'
app/controllers/users_controller.rb:58:incorrect_user'
test/integration/users_edit_test.rb:10:in `block in '
ERROR["test_successful_edit", UsersEditTest, 2015-11-05 04:35:59 -0600]
test_successful_edit#UsersEditTest (1446719759.28s)
NoMethodError: NoMethodError: undefined method correct_user?' for #<UsersController:0x007fcdefcea198>
app/controllers/users_controller.rb:58:incorrect_user'
test/integration/users_edit_test.rb:21:in block in <class:UsersEditTest>'
app/controllers/users_controller.rb:58:incorrect_user'
test/integration/users_edit_test.rb:21:in `block in '
ERROR["test_should_redirect_edit_when_logged_in_as_wrong_user", UsersControllerTest, 2015-11-05 04:35:59 -0600]
test_should_redirect_edit_when_logged_in_as_wrong_user#UsersControllerTest (1446719759.42s)
NoMethodError: NoMethodError: undefined method correct_user?' for #<UsersController:0x007fcdf529f778>
app/controllers/users_controller.rb:58:incorrect_user'
test/controllers/users_controller_test.rb:29:in block in <class:UsersControllerTest>'
app/controllers/users_controller.rb:58:incorrect_user'
test/controllers/users_controller_test.rb:29:in `block in '
ERROR["test_should_redirect_update_when_logged_in_as_wrong_user", UsersControllerTest, 2015-11-05 04:35:59 -0600]
test_should_redirect_update_when_logged_in_as_wrong_user#UsersControllerTest (1446719759.44s)
NoMethodError: NoMethodError: undefined method correct_user?' for #<UsersController:0x007fcdf531e118>
app/controllers/users_controller.rb:58:incorrect_user'
test/controllers/users_controller_test.rb:36:in block in <class:UsersControllerTest>'
app/controllers/users_controller.rb:58:incorrect_user'
test/controllers/users_controller_test.rb:36:in `block in '
32/32: [======================] 100% Time: 00:00:01, Time: 00:00:01
Finished in 1.70355s
32 tests, 64 assertions, 0 failures, 4 errors, 0 skips
Users_controller.rb
class UsersController < ApplicationController
before_action :logged_in_user, only: [:edit, :update]
before_action :correct_user, only: [:edit, :update]
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
log_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
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)
end
# Before Filters
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
flash[:danger] = "Please log in."
redirect_to login_url
end
end
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless correct_user?(#user)
end
end
User_controller_test
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
def setup
#user = users(:michael)
#other_user = users(:archer)
end
test "should get new" do
get :new
assert_response :success
end
test "should redirect edit when logged in" do
get :edit, id: #user
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect update when not logged in" do
patch :update, id: #user, user: { name: #user.name, email: #user.email }
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect edit when logged in as wrong user" do
log_in_as(#other_user)
get :edit, id: #user
assert flash.empty?
assert_redirected_to root_url
end
test "should redirect update when logged in as wrong user" do
log_in_as(#other_user)
patch :update, id: #user, user: { name: #user.name, email: #user.email }
assert flash.empty?
assert_redirected_to root_url
end
end
users.yml
michael:
name: Michael Example
email: michael#example.com
password_digest: <%= User.digest('password') %>
archer:
name: Sterling Archer
email: duchess#example.gov
password_digest: <%= User.digest('password') %>
users_edit_test
require 'test_helper'
class UsersEditTest < ActionDispatch::IntegrationTest
def setup
#user = users(:michael)
end
test "unsuccessful edit" do
log_in_as(#user)
get edit_user_path(#user)
assert_template 'users/edit'
patch user_path(#user), user: { name: "",
email: "foo#invalid",
password: "foo",
password_confirmation: "bar" }
assert_template 'users/edit'
end
test "successful edit" do
log_in_as(#user)
get edit_user_path(#user)
assert_template 'users/edit'
name = "Foo Bar"
email = "foo#bar.com"
patch user_path(#user), user: { name: name,
email: email,
password: "",
password_confirmation: "" }
assert_not flash.empty?
assert_redirected_to #user
#user.reload
assert_equal name, #user.name
assert_equal email, #user.email
end
end
I think that is all that would be needed to help. I have been struggling for some time now. This is my first post, sorry if it isn't easy to read.
correct_user? isn't defined anywhere.
redirect_to(root_url) unless correct_user?(#user)
Rails's syntactic sugar is going to look for a model flag correct_user or a boolean-returning function #correct_user? getter. I'm assuming that you're trying to validate that an update request from a user actually belongs to the correct user. To do this you must have current_user stored in a session, and then check the request param[:id] == current_user.id
Signup Test Validation not working. Here is the code.
test "valid signup information" do
get signup_path
assert_difference 'User.count', 1 do
post_via_redirect users_path, user:{
name: "Example User",
email: "user#invalid.com",
password: "password",
password_confirmation: "password "
}
end
assert_template 'users/show'
end
Here is the result of Minitest.
"User.count" didn't change by 1.
Expected: 1
Actual: 0
I have done the invalid test, it works fine. the valid path is not working, when i add 1 to the block.
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:success] = "Welcome to the app"
redirect_to user_url(#user)
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
Updated