2

I wanted to add some custom fields to the user registration so I overrode the devise registration controller. Now I'm trying to write a test that should (I would think) create a new, unregistered user. The test fails though and I'm not sure what to troubleshoot from here.

# note: this is a dump of `:valid_attributes` by my test
{"id"=>nil, "name"=>"Hans Swift", "email"=>"Hans.Swift_397@testing.com", "encrypted_password"=>"$2a$04$T6mRo3Dzcv6iV7Kdh52E6OA0tAX7nE4y3skV2jzkD9CLr5v8Dri1K", "reset_password_token"=>nil, "reset_password_sent_at"=>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, "confirmation_token"=>nil, "confirmed_at"=>nil, "confirmation_sent_at"=>nil, "unconfirmed_email"=>nil, "failed_attempts"=>0, "unlock_token"=>nil, "locked_at"=>nil, "created_at"=>nil, "updated_at"=>nil}
F
Failures:

  1) Users::RegistrationsController POST #create with valid params creates a new User
     Failure/Error:
       expect {
         puts valid_attributes
         post :create, {:user => valid_attributes}, valid_session
       }.to change(User, :count).by(1)

       expected #count to have changed by 1, but was changed by 0
     # ./spec/controllers/users/registrations_controller_spec.rb:19:in `block (4 levels) in <top (required)>'

Can anyone suggest a reason this test is failing to create a user? The application works properly and an email arrives in my inbox when I use the users/sign_up form.

./spec/controllers/users/registrations_controller_spec.rb

require 'rails_helper'
require 'factory_girl_rails'

RSpec.describe Users::RegistrationsController, type: :controller do

  describe "POST #create" do
    let(:valid_attributes) { FactoryGirl.build(:user_for_registration).attributes }
    let(:invalid_attributes) { User.get_invalid_user.attributes }
    let(:valid_session) { {} }

    # Help Devise map routes from the test back to the original controller.
    # See http://stackoverflow.com/questions/6659555/how-to-write-controller-tests-when-you-override-devise-registration-controller
    before :each do
      request.env['devise.mapping'] = Devise.mappings[:user]
    end

    context "with valid params" do
      it "creates a new User" do
        expect {
          puts valid_attributes
          post :create, {:user => valid_attributes}, valid_session
        }.to change(User, :count).by(1)
      end      
    end

  end

end

./app/controllers/users/registrations_controller.rb

class Users::RegistrationsController < Devise::RegistrationsController
  before_filter :configure_permitted_parameters, if: :devise_controller?

  protected

  # we have to explicitly permit params in overridden controllers like this
  # see https://github.com/plataformatec/devise#strong-parameters
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up) do |u|
      u.permit(:name, :email, :password, :password_confirmation)
    end
    devise_parameter_sanitizer.permit(:account_update) do |u|
      u.permit(:name, :email, :password, :password_confirmation, :current_password)
    end
  end
end

./spec/factories/users.rb

require 'ffaker'

FactoryGirl.define do
  factory :user do |u|
    @pass = "password"

    name FFaker::Name.name
    email { |me| "#{name.to_s.gsub(/\s/,'.')}_#{rand(1000).to_s}@testing.com" }
    password @pass

    factory :user_for_registration do
      password_confirmation @pass
    end

    factory :user_for_account_update do
      password_confirmation @pass
      current_password @pass
    end
  end
end
doub1ejack
  • 10,627
  • 20
  • 66
  • 125
  • 1
    I think you are passing the wrong parameters through the request. It looks like you should be passing `:name, :email, :password, :password_confirmation` rather than `encrypted_password` etc. I've not posted it as an answer as I'm not 100%. Check the logs and see what params are being sent. – DickieBoy Nov 25 '16 at 17:10
  • Yeah, that looks right. It seems I should be using `FactoryGirl::attributes_for(:user)` instead of `FactoryGirl::build(:user).attributes`. When I change that the test passes. Not sure when I started using FactoryGirl::build().. Well, good to know, thanks! – doub1ejack Nov 25 '16 at 20:38
  • Judging from this, `attributes_for` looks like it will give you the attributes after some of the filter chain has ran. You should probably look into it a little bit and post it as an answer. – DickieBoy Nov 28 '16 at 09:14

1 Answers1

3

You say that your test POSTs all these attributes:

{
  "id"=>nil,
  "name"=>"Hans Swift",
  "email"=>"Hans.Swift_397@testing.com",
  "encrypted_password"=>"$2a$04$T6mRo3Dzcv6iV7Kdh52E6OA0tAX7nE4y3skV2jzkD9CLr5v8Dri1K",
  "reset_password_token"=>nil,
  "reset_password_sent_at"=>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,
  "confirmation_token"=>nil,
  "confirmed_at"=>nil,
  "confirmation_sent_at"=>nil,
  "unconfirmed_email"=>nil,
  "failed_attempts"=>0,
  "unlock_token"=>nil,
  "locked_at"=>nil,
  "created_at"=>nil,
  "updated_at"=>nil
}

First, it is actually POSTing all those under a user key, so the controller's params are actually user[id], user[name], etc. Devise needs its params to be at the top level, like this:

post :create, valid_attributes, valid_session

Second, that big list of attributes is surely not what your view POSTs, and your controller only permits name, email, password, and password_confirmation. So I think after fixing the first problem, your test's request would still be rejected. To fix things, make sure your test only posts those four attributes.

Next time around, you can get good hints about test failures by running just the single test (rspec spec/controllers/users/registrations_controller_spec:123 where 123 is the test's line number), and watching your log/test.log.

Once you get that passing I would also write some tests verifying that those params are required.

Also I see you are passing valid_session, and I would remove that too. Devise won't let you register if you are already signed in. Even though it is empty, it still seems confusing to include it.

Paul A Jungwirth
  • 23,504
  • 14
  • 74
  • 93
  • Awesome, I didn't know about `log/test.log` - that's a huge help. – doub1ejack Nov 30 '16 at 12:30
  • Also, to your second point the problem there was that I was passing `FactoryGirl::build(:user).attributes` instead of `FactoryGirl::attributes_for(:user)` and that's why I have that long list (the database fields, actually) instead of the 4 fields I needed. – doub1ejack Nov 30 '16 at 12:31