34

Shouldn't I be able to see instance variables which are created in a controller action from within my rspect tests?

# /app/controllers/widget_controller.rb
...
def show
  @widget = ...
  puts "in controller: #{@widget}"
end
...

--

# /spec/controllers/widget_controller_spec.rb
RSpec.describe WidgetController, type: :controller do
...
describe "GET #show" do
  it "assigns the requested widget as @widget" do
    get :show, { :id => 1 } # this is just an example - I'm not hardcoding the id

    puts "in spec: #{@widget}"
  end
end
...

Here is the output I get when I run that spec:

controller: #<Widget:0x007f9d02aff090>
in spec:

Am I wrong in thinking that I should have access to @widget in my controller spec?

RobertJoseph
  • 7,968
  • 12
  • 68
  • 113

4 Answers4

41

Use the assigns method (*note: assigns is now deprecated. See bottom of my answer for info):

it "assigns the @widget"
  expect(assigns(:widget)).not_to be_nil
end

Of course, you can inspect widget as you'd like, but without seeing what @widget is from your provided controller code, I just checked if it was nil

If you're wanting to puts the widget, like in your example, just print it with assigns

puts assigns(:widget)

EDIT: assigns is now deprecated (see: https://apidock.com/rails/ActionController/TestProcess/assigns)

If you want to continue using assigns you will need to install the rails-controller-testing gem.

Otherwise, you could use what rails-controller-testing gem uses internally: @controller.view_assigns[]

Wes Foster
  • 8,770
  • 5
  • 42
  • 62
  • 3
    RuboCop complains about the usage of local variables in tests. That being said `controller.view_assigns['var_name']` works just fine without the `@` – Delfic Mar 16 '22 at 09:53
24

#assigns is deprecated. Here's a solution that avoids pulling in the rails-controller-testing gem

Though it can be a code smell to test instance variables in controllers, to upgrade older apps you can leverage #instance_variable_get.

Rspec example: expect(@controller.instance_variable_get(:@person).id).to eq(person_id)

stevepentler
  • 261
  • 2
  • 4
19

In Rails 5, assigns has now been split out into a new rails-controller-testing gem.

You can either install the gem to continue using assigns, or you could use what the rails-controller-testing gem uses internally, which@controller.view_assigns[].

Will Clarke
  • 3,480
  • 1
  • 16
  • 13
0

In new Rails versions with rails-controller-testing gem you can access to instance variables two ways:

  • with assigns[:key] / assigns['key']

  • with controller.view_assigns['key']

As you see assigns is ActiveSupport::HashWithIndifferentAccess, so you you can use both a string or symbol as key. It is formed with regular_writer method from the Hash, where all the keys with instance variables are strings, i.e. from @controller.view_assigns. But you can also access to controller inside tests

Here example

require 'rails_helper'

describe SomeController, type: :controller do
  before do
    assign(:some_var, 10)

    get :show, params: { id: 1 }
  end

  it 'assigns the @some_var'
    expect(assigns['some_var']).to eq 10
    expect(assigns[:some_var]).to eq 10

    expect(controller.view_assigns['some_var']).to eq 10
  end
end
mechnicov
  • 12,025
  • 4
  • 33
  • 56