Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are helper methods not working in view specs?

I have a conditional in my view based upon a helper method defined in my application controller, which I defined as a helper method.

<% if logged_in? %>

When my spec hits this, it says:

ActionView::Template::Error:
   undefined method `logged_in?' for #<#<Class:0x007fdc06b71aa8>:0x007fdc0b9b0930>

When I use my browser, it works perfect. I tried stubbing it, but I get another error when I try to stub it on the view or controller. This is what my test looks like:

it "has a logout link" do
  render
  expect(rendered).to have_link "Logout"
end

Here's my gemfile:

ruby "2.4.1"

gem "bootsnap", ">= 1.1.0", require: false
gem "coffee-rails", "~> 4.2"
gem "jbuilder", "~> 2.5"
gem "pg"
gem "puma", "~> 3.11"
gem "rails", "~> 5.2.1"
gem "rails-controller-testing"
gem "sass-rails", "~> 5.0"
gem "turbolinks", "~> 5"
gem "uglifier", ">= 1.3.0"

group :development, :test do
  gem "byebug", platforms: %i[mri mingw x64_mingw]
end

group :development do
  gem "listen", ">= 3.0.5", "< 3.2"
  gem "spring"
  gem "spring-watcher-listen", "~> 2.0.0"
  gem "web-console", ">= 3.3.0"
end

group :test do
  gem "capybara", ">= 2.15"
  gem "chromedriver-helper"
  gem "factory_bot_rails"
  gem "rspec-rails", "~> 3.7"
  gem "selenium-webdriver"
  gem "shoulda-matchers", "~> 3.1"
end
like image 828
thesimpledev Avatar asked Oct 14 '25 08:10

thesimpledev


2 Answers

Update to the accepted answer, it now seems necessary to use without_partial_double_verification to get around the ActionView::Base does not implement: field_name error.

Example code:

let(:logged_in) { true }

before(:each) do
  without_partial_double_verification do
    allow(view).to receive(:logged_in?).and_return(logged_in)
  end

  render
end

it 'has a logout link' do
  expect(rendered).to have_link 'Logout'
end

context 'with no logged user' do
  let(:logged_in) { false }

  it 'has a login link' do
    expect(rendered).to have_link 'Login'
  end
end
like image 132
Andrew Avatar answered Oct 17 '25 02:10

Andrew


View specs usually does not need to call the real helper (if you wan't to test the helper then you can do a helper spec).

It's faster and easier to just stub the helper method to return the value you want for your given test:

context 'with a logged user' do
  before(:each) do
    allow(view).to receive(:logged_in?).and_return(true)
  end

  it 'has a logout link' do
    render
    expect(rendered).to have_link 'Logout'
  end
end

context 'with no logged user' do
  before(:each) do
    allow(view).to receive(:logged_in?).and_return(false)
  end

  it 'has a login link' do
    render
    expect(rendered).to have_link 'Login'
  end
end

When you do model, controller, views, helper or any other isolated test, it does not have access to all the app since it's designed to run fast. For example: controller tests does not render views by default, model specs only tests the model, view specs do not have a context of the request nor session, etc.

You can have full real user tests using integration (feature) specs which runs the whole user interaction from beginning to end with access to everything, even controlling what the user clicks.

like image 25
arieljuod Avatar answered Oct 17 '25 02:10

arieljuod