Azuan
Azuan

Reputation: 928

Rspec controller not behaving as expected

I'm trying to run a unit testing on my API controllers in Rails. But it's not behaving like I thought it should.

This line works:

post '/api/sessions', params

This line however

post :create, params

will throw error

Failure/Error: post :create
 URI::InvalidURIError:
   bad URI(is not URI?): http://www.example.com:80create

I can use the first one, but would be good to know why doesn't the 2nd syntax works? Because I remember using that syntax in previous projects, and it works. I don't remember any special setup on my RSpec config.

Update: My codes

Api::ApiController

class Api::ApiController < ActionController::Base
  include ApplicationHelper
end

Api::SessionsController

module Api
  class SessionsController < Api::ApiController
    def create
      # find user and store in @user. I'm using jbuilder
      render status: 201
    end
  end
end

RSpec

require 'rails_helper'

describe Api::SessionsController, :type => :controller do
  describe '#POST' do
    it 'should retrieve user' do
      post '/api/sessions', params # this works
      post :create, params # this fails
    end
  end
end

route.rb

Rails.application.routes.draw do
  apipie
  devise_for :users
  root 'home#index'    

  namespace :api, defaults: { format: :json } do
    resources :sessions, only: [:create]
    resources :users, only: [:create]
    resources :trips, only: [:create]
  end
end


Based on the comment, I run raise method(:post).source_location.inspect at the beginning of the test, and this is what I get.

For this project, which currently failed:

RuntimeError:
   ["/Users/tokwan/.rvm/gems/ruby-2.2.2/gems/actionpack-4.2.2/lib/action_dispatch/testing/integration.rb", 335]

And for other project, where post :create works:

RuntimeError:
   ["/Users/tokwan/.rvm/gems/ruby-2.2.0/gems/actionpack-4.2.2/lib/action_controller/test_case.rb", 513]

Upvotes: 2

Views: 2279

Answers (1)

Frederick Cheung
Frederick Cheung

Reputation: 84114

TLDR: It sounds like you thing you are running a controller spec, but actually you are running a request spec.

Rspec can find out the type of a spec through 2 mechanisms: If you tell it in the metadata for the example / example group,

describe "something", type: :controller  do
  ..
end

or based on file location, if you have config.infer_spec_type_from_file_location! in your spec helper. It looks like your spec is tagged as controller, but it sounds like something is overriding it: perhaps it's in the wrong location, or something else is changing its type.

As a recap, the differences between controller & requests specs:

Controller specs

These live in spec/controllers, with type: controller. Rails calls these functional tests, but they are basically unit tests for individual controllers (especially with rspec, which by default skips the rendering of views).

Each spec tests a single invocation of a controller action. The invocation of the action doesn't use routing (although I think it is an error these days if there is no route mapped to the action). Rails provides helpers that will invoke the specified action with any desired parameters, e.g. get action: 'show', id: 1. Rack Middlewares and so on are skipped.

Request specs

These live in spec/requests, spec/api, and spec/integration. Rails calls them integration tests. These can span multiple requests and run through the entire middleware stack, render views etc. This isn't just a test of a single controller so you specify a path, rather than just an action, e.g. get '/articles/1'

Upvotes: 6

Related Questions