Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 4 + Devise: How to write a test for Devise Reset Password without RSpec?

For reasons outside of my control, I can't use RSpec for testing in my current project. I'm trying to test Devise Reset Password, and I can't seem to come up with something that works.

Here's what I have so far:

require 'test_helper'

class ResetPasswordTest < ActionDispatch::IntegrationTest
  setup do
    @user = users(:example)
  end

  test "reset user's password" do 
    old_password = @user.encrypted_password

    puts @user.inspect

    # This assertion works
    assert_difference('ActionMailer::Base.deliveries.count', 1) do
      post user_password_path, user: {email: @user.email}
    end

    # puts @user.reset_password_token => nil
    # Not sure why this doesn't assign a reset password token to @user

    patch "/users/password", user: {
      reset_password_token: @user.reset_password_token, 
      password: "new-password", 
      password_confirmation: "new-password",
    }

    # I get a success here, but I'm not sure why, since reset password token is nil.
    assert_response :success

    # This assertion doesn't work. 
    assert_not_equal(@user.encrypted_password, old_password)
  end

end

I've added some comments above to where things don't seem to be working. Does anyone have an insight, or an idea about how better to test this?

like image 308
justindao Avatar asked Nov 10 '14 16:11

justindao


2 Answers

Devise tests the PasswordsController internally. I wanted to test my PasswordsController with extra functionality and went to Devise's controller tests for help in building tests for Devise's default functionality, then adding assertions for my new functionality into the test case.

As noted in other answers, you must obtain the reset_password_token. Instead of parsing a delivered email as in another solution here, you could obtain the token in the same way as Devise:

setup do
  request.env["devise.mapping"] = Devise.mappings[:user]
  @user = users(:user_that_is_defined_in_fixture)
  @reset_password_token  = @user.send_reset_password_instructions
end

Check out Devise's solution for the PasswordControllerTest:

https://github.com/plataformatec/devise/blob/master/test/controllers/passwords_controller_test.rb.

like image 110
sealocal Avatar answered Sep 22 '22 09:09

sealocal


I figured out that you need to reload the user upon sending the password reset email and when putting the /user/password route. You also need to get the password token from the email as it is different from the one stored in the database.

require 'test_helper'

class ResetPasswordTest < ActionDispatch::IntegrationTest
  setup do
    @user = users(:example)
  end

  test "reset user's password" do 
    # store old encrypted password
    old_password = @user.encrypted_password

    # check to ensure mailer sends reset password email
    assert_difference('ActionMailer::Base.deliveries.count', 1) do
      post user_password_path, user: {email: @user.email}
      assert_redirected_to new_user_session_path
    end

    # Get the email, and get the reset password token from it
    message = ActionMailer::Base.deliveries[0].to_s
    rpt_index = message.index("reset_password_token")+"reset_password_token".length+1
    reset_password_token = message[rpt_index...message.index("\"", rpt_index)]

    # reload the user and ensure user.reset_password_token is present
    # NOTE: user.reset_password_token and the token pulled from the email
    # are DIFFERENT
    @user.reload
    assert_not_nil @user.reset_password_token

    # Ensure that a bad token won't reset the password
    put "/users/password", user: {
      reset_password_token: "bad reset token", 
      password: "new-password", 
      password_confirmation: "new-password",
    }

    assert_match "error", response.body
    assert_equal @user.encrypted_password, old_password

    # Valid password update
    put "/users/password", user: {
      reset_password_token: reset_password_token, 
      password: "new-password", 
      password_confirmation: "new-password",
    }

    # After password update, signed in and redirected to root path
    assert_redirected_to root_path

    # Reload user and ensure that the password is updated.
    @user.reload
    assert_not_equal(@user.encrypted_password, old_password)
  end

end
like image 39
justindao Avatar answered Sep 25 '22 09:09

justindao