简体   繁体   中英

How do I test an instance variable in a controller?

A couple of my mentors have told me to test a piece of code with controller spec even though the contents are being tested by some request specs. I am wondering if I can test whether an instance variable like @user can be set to someObject or nil from within an rspec controller test?

I've tried a couple different ways to test this so far, but my skills regarding rspec testing are to say the least, elementary. The first thing I tried was mocking out the current_user and the 'User.get_from_auth_user(current_user). The mocked object prints fine and outputs. The problem is... I want to be able to say something like expect(@user).to equal(mockedUser) but this never seems to work. I always get something like:

Expect does not equal received
expected is nil
received is { someMockedObject }

Here is the code I want to test.

class ApplicationController < ActionController::Base
  before_action :set_user

  def set_user
    return if current_user.nil?

    @user = User.get_from_auth_user(current_user)
  end
end

I want to know if I'm going down the right path or what would be the better alternative to fully test this chunk of code. I feel like the answer should be a simple one.

Your mentors are being stupid. While it can be good to understand controller specs as they are everywhere in legacy applications adding them to new applications is not a good practice.

For new Rails apps: we don't recommend adding the rails-controller-testing gem to your application. The official recommendation of the Rails team and the RSpec core team is to write request specs instead. Request specs allow you to focus on a single controller action, but unlike controller tests involve the router, the middleware stack, and both rack requests and responses. This adds realism to the test that you are writing, and helps avoid many of the issues that are common in controller specs.

DHH explains pretty well what is wrong with testing the instance variables of your controller and what you should be doing instead:

Testing what instance variables are set by your controller is a bad idea. That's grossly overstepping the boundaries of what the test should know about. You can test what cookies are set, what HTTP code is returned, how the view looks, or what mutations happened to the DB, but testing the innards of the controller is just not a good idea.

Which is why assigns was extracted to a separate gem. It all boils down to testing what your code does - not how it does it.

See:

Use assigns(:user) to get the instance variable @user . In your spec, @user is a variable on that test context, it's not the same @user from the controller.

https://relishapp.com/rspec/rspec-rails/docs/controller-specs

In addition to @chans and @arieljuod answers, it worth to mention in order to use assigns in your specs, you need to set up rails-controller-testing gem, because assigns is deprecated starting from Rails 5.

# File actionpack/lib/action_dispatch/testing/test_process.rb, line 6
def assigns(key = nil)
  raise NoMethodError,
    "assigns has been extracted to a gem. To continue using it,
    add `gem 'rails-controller-testing'` to your Gemfile."
end

The controller test case looks like this

describe 'GET #set_user' do
  let!(:mock_user) { 
      @mockuserObj = { id: 1, name: "username" }
  }
  before(:each) do
    controller.instance_variable_set(:current_user, @mockuserObj)
    get :set_user
  end
  context 'when current_user is present' do
    it "sets the current user" do
      expect(assigns(:user)).to eq(@mockuserObj)
    end
  end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM