Rspec undefined local variable or method root_path

2019-07-17 09:23发布

问题:

I'm starting to use Rspec, but when I run bundle exec rspec I get an error

 /spec/requests/pages_spec.rb:20:in `block (2 levels) in <top (required)>': undefined local
 variable or method `root_path' for #<Class:0x00000102e46850> (NameError)

I do not have Spork or Guard running so the question below shouldn't apply

undefined local variable or method `root_path' (Rspec Spork Guard)

I have added config.include Rails.application.routes.url_helpers in my spec_helper.rb file, but that did not help. undefined local variable or method `root_path' Hartl's Tutorial Chapter 5.3.2

Here's pages_spec.rb

require 'spec_helper'                                                                                                                                                       

describe "Pages" do                                                                                                                                                         
  describe "navigation" do                                                                                                                                                  

    def self.it_should_be_on(path_name, value=nil)                                                                                                                          
      before { visit path_name }                                                                                                                                            

      it "should be on #{path_name}" do                                                                                                                                     
        page.should have_link('Home')                                                                                                                                       
        page.should have_link('Inventory')                                                                                                                                  
        page.should have_link('FAQ')                                                                                                                                        
        page.should have_link('About Us')                                                                                                                                   
        page.should have_link('Location')                                                                                                                                   
        page.should have_link('Contact Us')                                                                                                                                 
        # page.should have_link('Login')                                                                                                                                    
      end                                                                                                                                                                   
    end                                                                                                                                                                     

    it_should_be_on root_path                                                                                                                                               
    it_should_be_on faq_path                                                                                                                                                
    it_should_be_on about_path                                                                                                                                              
    it_should_be_on location_path                                                                                                                                           
    it_should_be_on contact_path                                                                                                                                            
    # it_should_be_on login_path                                                                                                                                            
  end                                                                                                                                                                       
end       

spec_helper.rb

# This file is copied to spec/ when you run 'rails generate rspec:install'                                                                                                  
ENV["RAILS_ENV"] ||= 'test'                                                                                                                                                 
require File.expand_path("../../config/environment", __FILE__)                                                                                                              
require 'rspec/rails'                                                                                                                                                       
require 'rspec/autorun'                                                                                                                                                     
# Requires supporting ruby files with custom matchers and macros, etc,                                                                                                      
# in spec/support/ and its subdirectories.                                                                                                                                  
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }                                                                                                         

# Checks for pending migrations before tests are run.                                                                                                                       
# If you are not using ActiveRecord, you can remove this line.                                                                                                              
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)                                                                                                 

RSpec.configure do |config|                                                                                                                                                 
  # ## Mock Framework                                                                                                                                                       
  #                                                                                                                                                                         
  # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:                                                                                             
  #                                                                                                                                                                         
  # config.mock_with :mocha                                                                                                                                                 
  # config.mock_with :flexmock                                                                                                                                              
  # config.mock_with :rr                                                                                                                                                    

  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures                                                                                              
  config.fixture_path = "#{::Rails.root}/spec/fixtures"                                                                                                                     

  # If you're not using ActiveRecord, or you'd prefer not to run each of your                                                                                               
  # examples within a transaction, remove the following line or assign false                                                                                                
  # instead of true.                                                                                                                                                        
  config.use_transactional_fixtures = true                                                                                                                                  

  # If true, the base class of anonymous controllers will be inferred                                                                                                       
  # automatically. This will be the default behavior in future versions of                                                                                                  
  # rspec-rails.                                                                                                                                                            
  config.infer_base_class_for_anonymous_controllers = false                                                                                                                 

  # Run specs in random order to surface order dependencies. If you find an                                                                                                 
  # order dependency and want to debug it, you can fix the order by providing                                                                                               
  # the seed, which is printed after each run.                                                                                                                              
  #     --seed 1234                                                                                                                                                         
  config.order = "random"                                                                                                                                                   
  config.include Capybara::DSL                                                                                                                                              
  config.include Rails.application.routes.url_helpers                                                                                                                       
end                                              

Update After reading about shared_examples, I tried this out successfully. Is there a better way to write this test? I ended up separating out the pages into individual pages like Home page etc.

require 'spec_helper'                                                               

describe "Pages" do                                                                 

  subject { page }                                                                  

  shared_examples "navigation" do |path_name|                                       
    before { visit send( path_name) }                                               

    describe "navigation links should be on #{path_name}" do                        

      it { should have_link('Home') }                                               
      it { should have_link('Inventory') }                                          
      it { should have_link('FAQ') }                                                
      it { should have_link('About Us') }                                           
      it { should have_link('Location') }                                           
      it { should have_link('Contact Us') }                                         
      # it { should have_link('Login') }                                            
    end                                                                             
  end                                                                               

  describe "Home Page" do                                                           
    include_examples "navigation", :root_path                                       
  end                                                                               
end            

回答1:

To save your structure - you can change the code like this:

require 'spec_helper'                                                                                                                                                       

describe "Pages" do                                                                                                                                                         
  describe "navigation" do                                                                                                                                                  

    shared_examples_for 'main page' do |path_name|                                                                                                                         
      before { visit send(path_name) }                                                                                                                                            

      it "should be on #{path_name}" do                                                                                                                                     
        page.should have_link('Home')                                                                                                                                       
        page.should have_link('Inventory')                                                                                                                                  
        page.should have_link('FAQ')                                                                                                                                        
        page.should have_link('About Us')                                                                                                                                   
        page.should have_link('Location')                                                                                                                                   
        page.should have_link('Contact Us')                                                                                                                                 
        # page.should have_link('Login')                                                                                                                                    
      end                                                                                                                                                                   
    end                                                                                                                                                                     

    it_should_behave_like 'main_page', :root_path                                                                                                                                               
    it_should_behave_like 'main_page', :faq_path                                                                                                                                                
    it_should_behave_like 'main_page', :about_path                                                                                                                                              
    it_should_behave_like 'main_page', :location_path                                                                                                                                           
    it_should_behave_like 'main_page', :contact_path                                                                                                                                            
    # it_should_behave_like 'main_page', :login_path                                                                                                                                            
  end                                                                                                                                                                       
end    

because "paths are not defined at the class level in specs"(c) You cannot call path methods in spec class. It should be in it block. And your structure is not perfect. It will be better to put code in modules and then include it, if you want to avoid the duplication.



回答2:

None of the Rails helpers are available at the top level of RSpec's describe block. They are only available within the lower level blocks (e.g. let, before, it, etc.).

If you want to share code such as this across examples, you can use a shared_context or a shared_example, as described in the RSpec documentation, or switch to using symbols as parameters while at the describe level and defer interpretation of them as methods until you're within the lower level blocks, as shown in the answer from @IharDrozdov.