Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Undefined method 'patch' for #<Rspec::Core - Rails Tutorial Chapter 9

I'm getting the following error, and can't figure out how to fix it. There are some other pages on Stack Overflow addressing similar issues, but these don't seem to apply, or at least I'm not savvy enough to work it out.

When I run the authentication_pages_spec, the issue appears to be with the use of "patch", resulting in the failure below. Note that I've experimented with replacing "patch" with "put" and get the same results.

Failures:

1) Authentication authorization for non-signed-in users in the Users controller submitting to the update action 
 Failure/Error: before { patch user_path(user) }
 NoMethodError:
   undefined method `patch' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_3::Nested_1::Nested_1::Nested_2:0x007fa7e4df1e50>
 # ./spec/features/authentication_pages_spec.rb:59:in `block (6 levels) in <top (required)>'

Finished in 0.34392 seconds
2 examples, 1 failure

NOTE: based on discussion on Stack Overflow here and the blog it references here I elected to change my spec/requests folder to spec/features, since this appears to be what Capybara requires after version 2.0.

Here is my Gemfile: source 'https://rubygems.org' ruby '2.0.0' #ruby-gemset=railstutorial_rails_4_0

gem 'rails', '4.0.0'
gem 'bootstrap-sass', '2.3.2.0'
gem 'pg', '0.15.1'
gem 'bcrypt-ruby', '3.0.1'

group :development, :test do
  gem 'rspec-rails', '2.13.1'
  gem 'guard-rspec', '2.5.0'
  gem 'spork-rails', github: 'sporkrb/spork-rails'
  gem 'guard-spork', '1.5.0'
  gem 'childprocess', '0.3.9'
end

group :test do
  gem 'selenium-webdriver', '2.0.0'
  gem 'capybara', '2.1.0'
  gem 'growl', '1.0.3'
  gem 'factory_girl_rails', '4.2.1'
  gem 'cucumber-rails', '1.3.0', :require => false
  gem 'database_cleaner', github: 'bmabey/database_cleaner'
end

gem 'sass-rails', '4.0.0'
gem 'uglifier', '2.1.1'
gem 'coffee-rails', '4.0.0'
gem 'jquery-rails', '2.2.1'
gem 'turbolinks', '1.1.1'
gem 'jbuilder', '1.0.2'

group :doc do
  gem 'sdoc', '0.3.20', require: false
end

group :production do
  gem 'pg', '0.15.1'
  gem 'rails_12factor', '0.0.2'
end

I modified my Guardfile to use the spec/features folder instead of the spec/requests folder:

    require 'active_support/inflector'

guard 'spork',  :cucumber_env => { 'RAILS_ENV' => 'test' },
                :rspec_env    => { 'RAILS_ENV' => 'test' } do

  watch('config/application.rb')
  watch('config/environment.rb')
  watch('config/environments/test.rb')
  watch(%r{^config/initializers/.+\.rb$})
  watch('Gemfile')
  watch('Gemfile.lock')
  watch('spec/spec_helper.rb') { :rspec }
  watch('test/test_helper.rb') { :test_unit }
  watch(%r{features/support/}) { :cucumber }
end

guard 'rspec', all_after_pass: false, cli: '--drb' do
  watch(%r{^spec/.+_spec\.rb$})
  watch(%r{^lib/(.+)\.rb$})     { |m| "spec/lib/#{m[1]}_spec.rb" }
  watch('spec/spec_helper.rb')  { "spec" }

  # Rails example
  watch(%r{^app/(.+)\.rb$})                           { |m| "spec/#{m[1]}_spec.rb" }
  watch(%r{^app/(.*)(\.erb|\.haml)$})                 { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
  watch(%r{^app/controllers/(.+)_(controller)\.rb$})  { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
  watch(%r{^spec/support/(.+)\.rb$})                  { "spec" }
  watch('config/routes.rb')                           { "spec/routing" }
  watch('app/controllers/application_controller.rb')  { "spec/controllers" }

  # Capybara features specs
  watch(%r{^app/views/(.+)/.*\.(erb|haml)$})          { |m| "spec/features/#{m[1]}_spec.rb" }

  # Turnip features and steps
  watch(%r{^spec/acceptance/(.+)\.feature$})
  watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$})   { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }

  # Custom Rails Tutorial specs
  watch(%r{^app/controllers/(.+)_(controller)\.rb$}) do |m|
    ["spec/routing/#{m[1]}_routing_spec.rb",
    "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb",
    "spec/acceptance/#{m[1]}_spec.rb",
    (m[1][/_pages/] ? "spec/features/#{m[1]}_spec.rb" :
                      "spec/features/#{m[1].singularize}_pages_spec.rb")]
  end
  watch(%r{^app/controllers/sessions_controller\.rb$}) do |m|
    "spec/features/authentication_pages_spec.rb"
  end
end

Users controller here:

class UsersController < ApplicationController
    attr_accessor :name, :email
  before_action :signed_in_user, only: [:edit, :update]

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      sign_in @user
        flash[:success] = "Welcome to the Sample App!"
        redirect_to @user
    else
        flash[:error] = "Oops!"
        render 'new'
    end
  end

  def edit
    @user = User.find(params[:id])
  end

  def update
    @user = User.find(params[:id])
    if @user.update_attributes(user_params)
      flash[:success] = "Profile updated"
      sign_in @user
      redirect_to @user
    else
      render 'edit'
    end
  end

  private

    def user_params
        params.require(:user).permit(:name, 
                        :email, :password, :password_confirmation)
    end

    # Before filters

    def signed_in_user
      redirect_to signin_url, notice: "Please sign in." unless signed_in?
    end
end

Spec helper file here:

require 'rubygems'
require 'spork'
#uncomment the following line to use spork with the debugger
#require 'spork/ext/ruby-debug'


Spork.prefork do
  ENV["RAILS_ENV"] ||= 'test'
  require File.expand_path("../../config/environment", __FILE__)
  require 'rspec/rails'
  require 'rspec/autorun'

  Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
  ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)

  RSpec.configure do |config|

    config.fixture_path = "#{::Rails.root}/spec/fixtures"

    config.use_transactional_fixtures = true

    config.infer_base_class_for_anonymous_controllers = false

    config.order = "random"

    config.include Capybara::DSL

    # Added below myself, troubleshooting an 'undefined method 'patch'' message
    config.include Rails.application.routes.url_helpers   
  end
end

Spork.each_run do
  # This code will be run each time you run your specs.

end

Finally, for good measure, the config/routes.rb file is here:

SampleApp::Application.routes.draw do
  resources :users
  resources :sessions, only: [:new, :create, :destroy]

  root 'static_pages#home'

  match '/signup',    to: 'users#new',             via: 'get'
  match '/signin',    to: 'sessions#new',          via: 'get'
  match '/signout',   to: 'sessions#destroy',      via: 'delete'
  match '/help',      to: 'static_pages#help',     via: 'get'
  match '/about',     to: 'static_pages#about',    via: 'get'
  match '/contact',   to: 'static_pages#contact',  via: 'get'

There is a similar discussion on Stack Overflow here, but the answer on that post is to move the tests from ./spec/requests to ./spec/features - which I've already done.

Another related discussion here, where the answer is to use require 'spec_helper' in the top of the feature file...which I already have.

One last observation: seems like much of the discussion on this topic indicates the issue is with Capybara needing tests located in the spec/features folder for it to be able to access the URL helpers - but my authentication_pages_spec.rb file seems to have no problem with 'visit'.

Appreciate any insight! The Rails Tutorial for Rails 4.0 is in beta, have been trying to figure out if this is a bug.

* UPDATE *

Got everything passing green after implementing Peter's suggestion. Following is the revised code from the authentication_pages_spec file (partial, just the "authorization" section):

  describe "authorization", type: :request do

    .
    .
    .

    describe "as wrong user" do
      let(:user) { FactoryGirl.create(:user) }
      let(:wrong_user) {FactoryGirl.create(:user, email: "[email protected]") }
      before { sign_in user, no_capybara: true }

      describe "visiting Users#edit page" do
        before { visit edit_user_path(wrong_user) }
        it { should_not have_title(full_title('Edit user')) }
      end

      describe "submitting a PATCH request to the Users#update action" do
        before { patch user_path(wrong_user) }
        #  NOTE:  Rails Tutorial uses root_url below not root_path
        specify { expect(response).to redirect_to(root_path) }
      end
    end
  end
end
like image 557
Scro Avatar asked Aug 17 '13 13:08

Scro


1 Answers

get, put, patch, etc. are only defined for :controller and :request specs, not for a :feature spec, which your authentication_pages_spec.rb test implicitly became when you changed its location. If you include type: :request in your top level describe, it should work.

like image 123
Peter Alfvin Avatar answered Oct 05 '22 23:10

Peter Alfvin