Skip to content

NoMethodError: undefined method `[]=' for nil:NilClass in unit test #839

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
davidjconnolly opened this issue Feb 27, 2017 · 9 comments
Closed

Comments

@davidjconnolly
Copy link

Hi, thanks for creating this awesome gem!

I'm trying to unit test my controllers and am getting an error I'm having a really hard time debugging. I've got devise_token_auth configured and am using all the default rails 5 testing gems.

I'm doing what I think is a pretty vanilla test case where I just want to get a list of companies for the currently logged in user. I've verified that the objects are being set up correctly and tried putting loggers/byebug in the controller, however the execution path never makes it that far. Something in my get call is failing but I have no idea what the error it's throwing means: NoMethodError: undefined method []=' for nil:NilClass`

Any suggestions?

test_helper.rb:

ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'

class ActiveSupport::TestCase
  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
  fixtures :all

  include FactoryGirl::Syntax::Methods
end

class ActionDispatch::IntegrationTest
  include Devise::Test::IntegrationHelpers
end

companies_controller_test.rb

require 'test_helper'

class CompaniesControllerTest < ActionDispatch::IntegrationTest

  setup do
    @user = create(:user)
    @company = create(:company)
    @permission = create(:permission, { user: @user, model: @company})
  end

  test "should get company" do
    sign_in @user

    get companies_url
  end

end

rake test output:

I, [2017-02-27T16:50:59.615092 #23039]  INFO -- : ------------------------------------------------
I, [2017-02-27T16:50:59.615143 #23039]  INFO -- : CompaniesControllerTest: test_should_get_company
I, [2017-02-27T16:50:59.615162 #23039]  INFO -- : ------------------------------------------------
I, [2017-02-27T16:50:59.884915 #23039]  INFO -- : Started GET "/companies" for 127.0.0.1 at 2017-02-27 16:50:59 -0700
E

Error:
CompaniesControllerTest#test_should_get_company:
NoMethodError: undefined method `[]=' for nil:NilClass
    test/controllers/companies_controller_test.rb:14:in `block in <class:CompaniesControllerTest>'
@davidjconnolly davidjconnolly changed the title NoMethodError: undefined method `[]=' for nil:NilClass NoMethodError: undefined method `[]=' for nil:NilClass in unit test Feb 27, 2017
@Blitzkev
Copy link
Contributor

Hey, I'm trying to do the same thing you are basically (Except starting with IntegrationTests with registration), and I had that error before but I no longer do.

This is my test_helper.rb:

ENV['RAILS_ENV'] = 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'

class ActiveSupport::TestCase
  include FactoryGirl::Syntax::Methods
  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
  fixtures :all

  # Add more helper methods to be used by all tests here...
end


class ActionController::TestCase
  include FactoryGirl::Syntax::Methods
  include Devise::Test::ControllerHelpers

  setup do
    @request.env['devise.mapping'] = Devise.mappings[:user]
  end
end

and my auth_controller_test.rb:

require 'test_helper'

class AuthControllerTest < ActionDispatch::IntegrationTest
  #include Devise::TestHelpers
  setup do
    # Create a user with factory girl
    @user = build(:user)
  end

  test "should create new user with sign_up" do
    pw = Faker::Internet.password(12, 20, true, true)
    assert_difference('User.count') do
      post user_registration_url, params: { email: @user.email, password: pw, password_confirmation: pw }, as: :json
      pp response.status
    end

    assert_response 201
  end
end

I keep getting 307 as response code from the post.

@davidjconnolly
Copy link
Author

Thanks @Blitzkev, what did you do to fix the []= for nil error? When you say you keep getting a 307 you mean where you assert_response 201?

@Blitzkev
Copy link
Contributor

Hey, I think adding

  setup do
    @request.env['devise.mapping'] = Devise.mappings[:user]
  end

Fixed that particular error for me, but like you said it was hard to troubleshoot.

The 307 is from the post user_registration_url line, which makes the User.count assert fail (no user created)

@Blitzkev
Copy link
Contributor

Blitzkev commented Feb 28, 2017

@davidjconnolly :
I was able to figure out my error, and then recreate an error similar. Since I have force_ssl on, I had to specify https for all test requests (hence the 307 redirecting to ssl)

# test/controllers/auth_controller_test.rb
require 'test_helper'

class AuthControllerTest < ActionDispatch::IntegrationTest
  setup do
    # Create a user with factory girl
    @user = build(:user)
  end

  test "should create new user with sign_up" do
    pw = Faker::Internet.password(12, 20, true, true)
    assert_difference('User.count') do
      post user_registration_url, params: { email: @user.email, password: pw, password_confirmation: pw }, as: :json
    end

    assert_response 200
  end
end
# test/test_helper.rb
ENV['RAILS_ENV'] = 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'

class ActiveSupport::TestCase
  include FactoryGirl::Syntax::Methods
  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
  fixtures :all

  # Add more helper methods to be used by all tests here...
  setup do
    @request.env['HTTPS'] = 'on'
  end
end


class ActionController::TestCase
  include FactoryGirl::Syntax::Methods
  include Devise::Test::ControllerHelpers

  setup do
    @request.env['devise.mapping'] = Devise.mappings[:user]
    @request.env['HTTPS'] = 'on'
  end
end
Run options: -vb --seed 61463

# Running:

D, [2017-02-28T23:47:56.336212 #10297] DEBUG -- :    (0.3ms)  BEGIN
I, [2017-02-28T23:47:56.336478 #10297]  INFO -- : ------------------------------------------------------------
I, [2017-02-28T23:47:56.336624 #10297]  INFO -- : AuthControllerTest: test_should_create_new_user_with_sign_up
I, [2017-02-28T23:47:56.336822 #10297]  INFO -- : ------------------------------------------------------------
D, [2017-02-28T23:47:56.757794 #10297] DEBUG -- :    (0.4ms)  ROLLBACK
AuthControllerTest#test_should_create_new_user_with_sign_up = 0.42 s = E


Error:
AuthControllerTest#test_should_create_new_user_with_sign_up:
NoMethodError: undefined method `env' for nil:NilClass
    /home/andes/test/test_helper.rb:12:in `block in <class:TestCase>'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:396:in `instance_exec'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:396:in `block in make_lambda'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:169:in `block (2 levels) in halting'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:547:in `block (2 levels) in default_terminator'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:546:in `catch'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:546:in `block in default_terminator'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:170:in `block in halting'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:454:in `block in call'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:454:in `each'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:454:in `call'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:101:in `__run_callbacks__'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:750:in `_run_setup_callbacks'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:90:in `run_callbacks'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activesupport-5.0.1/lib/active_support/testing/setup_and_teardown.rb:41:in `before_setup'
    /usr/local/rvm/gems/ruby-2.4.0/gems/activerecord-5.0.1/lib/active_record/fixtures.rb:853:in `before_setup'
    /usr/local/rvm/gems/ruby-2.4.0/gems/actionpack-5.0.1/lib/action_dispatch/testing/integration.rb:401:in `before_setup'
    /usr/local/rvm/gems/ruby-2.4.0/gems/railties-5.0.1/lib/rails/test_help.rb:40:in `before_setup'


bin/rails test test/controllers/auth_controller_test.rb:10


Finished in 0.428388s, 2.3343 runs/s, 0.0000 assertions/s.

1 runs, 0 assertions, 0 failures, 1 errors, 0 skips

This was caused because @request doesn't exist in non controller cases, so I've amended that.

Try running tests with the -vb option to see verbose output + full backtrace

@Blitzkev
Copy link
Contributor

Blitzkev commented Mar 1, 2017

I was unable to change the requests environment variables, I think this is no longer supported in ActionDispatch::IntegrationTest style controller tests... Which may be what include Devise::Test::IntegrationHelpers is attempting to do. I got my tests to pass by disabling ssl in test.rb environment file.

Would be good to hear from devise_token_auth devs with respect to this.

@davidjconnolly
Copy link
Author

Thanks @Blitzkev, I still can't get rid of the NoMethodError: undefined method '[]=' for nil:NilClass error.

If I add:

 setup do
    @request.env['devise.mapping'] = Devise.mappings[:user]
  end

To my class ActionDispatch::IntegrationTest block in my test helper, I get the same NoMethodError: undefined method 'env' for nil:NilClass error you mentioned.

Here is my test_helper.rb

ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'

class ActiveSupport::TestCase
  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
  fixtures :all

  include FactoryGirl::Syntax::Methods
end

class ActionDispatch::IntegrationTest
  include Devise::Test::IntegrationHelpers
end

And my controller unit test:

require 'test_helper'

class CompaniesControllerTest < ActionDispatch::IntegrationTest

  setup do
    @user = create(:user)
    @company = create(:company)
    @permission = create(:permission, { user: @user, model: @company})
  end

  test "should get company" do
    sign_in @user
    get companies_url
  end

end

I experimented with adding config.force_ssl = true to my test.rb file. If I set it to true, I just get a 301 response. If I set it to false, I get the same NoMethodError: undefined method '[]=' for nil:NilClass error that's been plaguing me.

I also wasn't able to run rake test -vb, I get an OptionParser::AmbiguousOption: ambiguous option: -b error. Instead I added the following to my test.rb file which seems to give me a more verbose (albeit not that much more useful) output:

config.logger = Logger.new(STDOUT)
config.log_level = :INFO

Any suggestions?

@Blitzkev
Copy link
Contributor

Blitzkev commented Mar 1, 2017

@davidjconnolly

I assume you're using Rails 5, if so the new preferred syntax is to use rails instead of rake, so your run command would be rails t -vb

If your main application doesn't use SSL, then don't worry about messing with those settings.

I don't think Devise::Test::IntegrationHelpers work in rails 5, I created my own helper and was able to get things working. Here are some snippets:

# test/test_helper.rb
ENV['RAILS_ENV'] = 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'

# Takes user object and their password, and returns headers we need for valid requests
# If testing multiple requests afterwards, you'll need to keep trak of changing headers
module SignInHelper
  def sign_in_as(user, password)
    post user_session_url, params: { email: user.email, password: password }, as: :json
    return response.headers.slice("client", "uid", "access-token")
  end
end

class ActiveSupport::TestCase
  include FactoryGirl::Syntax::Methods
  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
  fixtures :all

  # Add more helper methods to be used by all tests here...
end


class ActionController::TestCase
  include FactoryGirl::Syntax::Methods
end

class ActionDispatch::IntegrationTest
  include SignInHelper
end
# test/controllers/api/v1/users_controller_test.rb
require 'test_helper'

module Api::V1
  class UsersControllerTest < ActionDispatch::IntegrationTest
    setup do
      # Create a user with factory girl
      @pw = Faker::Internet.password(12, 20, true, true)
      email = Faker::Internet.free_email
      @user = create( :user, email: email, uid: email, password: @pw,
                                          password_confirmation: @pw )
    end

    test 'request should fail without signing in' do
      get api_v1_users_url, as: :json
      assert_response :unauthorized
    end

    test 'should get index' do
      tokens = sign_in_as( @user, @pw )
      get api_v1_users_url, headers: tokens, as: :json
      #get api_v1_users_url, as: :json - this would fail.

      assert_response 200
    end

  end # class
end # module

@davidjconnolly
Copy link
Author

I see, that worked, thanks for the explanation @Blitzkev. I'm coming from Rails 4 a few years back and apparently have some catching up to do!

FYI my controller/model tests are passing without:

class ActionController::TestCase
  include FactoryGirl::Syntax::Methods
end

Are you using ActionDispatch for all your controller/integration testing? What tests do you have that inherit ActionController?

Thanks again!

@zachfeldman
Copy link
Contributor

Seems like we have an explanation here and also a lack of recent activity. Closing out for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants