Rails integration test with the devise gem
Asked Answered
E

5

8

I want to write an rails integration test (with ActionDispatch::IntegrationTest). I am using devise for authentication and machinist for test models. I cannot successfully sign in.

Here is a simple example:

class UserFlowsTest < ActionDispatch::IntegrationTest
  setup do
    User.make
  end
  test "sign in to the site" 
    # sign in
    post_via_redirect 'users/sign_in', :email => '[email protected]', :password => 'qwerty'    
    p flash
    p User.all
end

Here is the debug output:

Loaded suite test/integration/user_flows_test
Started
{:alert=>"Invalid email or password."}
[#<User id: 980190962, email: "", encrypted_password: "", password_salt: "", reset_password_token: nil, remember_token: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, created_at: "2010-11-27 16:44:10", updated_at: "2010-11-27 16:44:10">, #<User id: 980190963, email: "[email protected]", encrypted_password: "$2a$10$vYSpjIfAd.Irl6eFvhJL0uAwp4qniv5gVl4O.Hnw/BUR...", password_salt: "$2a$10$vYSpjIfAd.Irl6eFvhJL0u", reset_password_token: nil, remember_token: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, created_at: "2010-11-27 17:09:13", updated_at: "2010-11-27 17:09:13">]
"/unauthenticated"
F

Here is my blueprints.rb:

require 'machinist/active_record'
require 'sham'

User.blueprint do
  email {"[email protected]"}
  password {"qwerty"}
end
Errantry answered 27/11, 2010 at 17:20 Comment(2)
I recently began reading Rails Test Prescriptions (pragprog.com/titles/nrtest/rails-test-prescriptions). Fairly early in the book, Noel mentions he will be using Devise for authentication. I jumped over to github.com to look at his sample application (github.com/noelrappin/huddle) to see if he solves this problem. While he doesn't feature any integration tests I'm posting it here for reference.Errantry
I've also switched (back) to using AuthLogic (github.com/binarylogic/authlogic) for authentication in my application. I've used it before and like it much better than Devise.Errantry
V
10

The reason it doesn't work is devise creates form field names as

'user[email]'
'user[password]'

and post_via_redirect expects those names as arguments. So following statement would make a successful login.

post_via_redirect 'users/sign_in', 'user[email]' => '[email protected]', 'user[password]' => 'qwerty' 
Venule answered 7/5, 2013 at 21:15 Comment(1)
Just in case it helps - in my case because it was an integration test of JSON only post_via_redirect 'users/sign_in', user: { email: "[email protected]", password: "qwerty" }, format: :json worked.Election
L
8

First of all, with devise, you may have to "confirm" the user.

you can do something like this:

user = User.make!
user.confirmed_at = Time.now
user.save!

Here is an example without Machinist (but you just have to replace the user creation code portion with the part above):

into test_integration_helper:

require "test_helper"
require "capybara/rails"

    module ActionController
      class IntegrationTest
        include Capybara

        def sign_in_as(user, password)
           user = User.create(:password => password, :password_confirmation => password, :email => user)
           user.confirmed_at = Time.now 
           user.save!
           visit '/'
           click_link_or_button('Log in')
           fill_in 'Email', :with => user.email
           fill_in 'Password', :with => password
           click_link_or_button('Sign in')
           user      
         end 
         def sign_out
            click_link_or_button('Log Out')   
         end
      end
    end

And into your integration_test:

require 'test_integration_helper'

class UsersIntegrationTest < ActionController::IntegrationTest

  test "sign in and then sign out" do 
    #this helper method is into the test_integration_helper file                   
    sign_in_as("[email protected]", "monkey")         
    assert page.has_content?('Signed in successfully'), "Signed in successfully"

    sign_out         
    assert page.has_content?('Signed out successfully'), "Signed out successfully" 
  end
end
Lothair answered 3/12, 2010 at 20:34 Comment(1)
Thanks for response. I'll have to dig up that project again and see how this works. +1 for now.Errantry
A
3

Since you are using ActionDispatch::IntegrationTest, you can include the Devise::Test::IntegrationHelpers module and use the sign_in method instead (which you can pass a user to):

class UserFlowsTest < ActionDispatch::IntegrationTest
  include Devise::Test::IntegrationHelpers

  test "sign in to the site" do
    sign_in users(:one)
  end
end
Amend answered 19/10, 2018 at 15:11 Comment(3)
What is the definition of :one? Where is devise getting this from? I'm getting NoMethodError: undefined method users' for #<UploadsTest:0x00007f6564c7cf88>`Swordfish
@Swordfish : That's not a method specific to Devise ; that's a fixture method provided by Rails. Here, users is the name of the model used to represent users and :one is the name of the entry in the fixture file.Amend
That's correct, I had to create a file called users.yml at test/fixtures/user.yml and define a user called one in yml format. Thanks!Swordfish
T
2

I don't have a sample integration test in the book for Devise, but I think the code in the step definition in the Cucumber chapter will also work if you have Webrat/Capybara installed:

@user = User.create!(:email => "[email protected]",  
            :password => "password",  
            :password_confirmation => "password")
visit "login"  
fill_in("user_email", :with => @user.email)  
fill_in("user_password", :with => "password") 
click_button("Sign In")
Tensimeter answered 3/12, 2010 at 20:0 Comment(1)
Thanks for response. I'll have to dig up that project again and see how this works. +1 for now. Also, thanks for the good read :)Errantry
S
1

In case anyone else has a similar problem...

I was in a similar situation (migrating from Authlogic), and had this exact same problem. The issue was in the devise.rb initializer. By default it sets up your test environment to use only 1 stretch during the encryption/decryption of passwords. I honestly don't know what a stretch is, but for Devise to work with old Authlogic encrypted passwords, the stretches has to be 20. Since I was trying to sign in a user that originally had their password encrypted by Authlogic in my test, Devise needs to use 20 stretches in test also. I changed the config like below:

# Limiting the stretches to just one in testing will increase the performance of
# your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
# a value less than 10 in other environments.
-  config.stretches = Rails.env.test? ? 1 : 20
+  config.stretches = 20
Sidesaddle answered 29/8, 2012 at 16:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.