Rspec / Capybara: Testing if a controller method is called - ruby-on-rails

Given I set up a HomeController with an index action
class HomeController < ApplicationController
def index
#users = User.all
end
end
and routed to it via the root path,
root :to => "home#index"
why does this request spec fail
it 'should called the home#index action' do
HomeController.should_receive(:index)
visit root_path
end
with the following message
Failure/Error: HomeController.should_receive(:index)
(<HomeController (class)>).index(any args)
expected: 1 time
received: 0 times
? Is it because the index method is called as a instance method instead of a class method?

I'm not sure exactly what you want to test, and I think there's some confusion as to what methods can be used where, so I'll try and give examples of Routing specs, Request Specs, Controller specs, and Feature specs, and hopefully one of them will be appropriate for you.
Routing
If you want to make sure that your root path gets routed to the home#index action, a routing spec may be appropriate:
spec/routing/routing_spec.rb
describe "Routing" do
it "routes / to home#index" do
expect(get("/")).to route_to("home#index")
end
end
Request
If you want to make sure that the index template gets rendered on a request to your root path, a request spec may be appropriate:
spec/requests/home_requests_spec.rb
describe "Home requests" do
it 'successfully renders the index template on GET /' do
get "/"
expect(response).to be_successful
expect(response).to render_template(:index)
end
end
Controller
If you want to make sure that the index template gets rendered on a request to the index action of your HomeController, a controller spec may be appropriate (and quite similar to a request spec in this case, but focused exclusively on the controller):
spec/controllers/home_controller_spec.rb
describe HomeController do
describe "GET index" do
it "successfully renders the index template" do
expect(controller).to receive(:index) # this line probably of dubious value
get :index
expect(response).to be_successful
expect(response).to render_template(:index)
end
end
end
Feature
If you want to make sure the page rendered by home#index has some specific content, a feature spec may be appropriate (and also the only place you can use Capybara methods like visit, depending on your Rails/RSpec version):
spec/features/home_features_spec.rb
feature "Index page" do
scenario "viewing the index page" do
visit root_path
expect(page).to have_text("Welcome to my awesome index page!")
end
end

class MyController < ApplicationController
def index
my_method
end
def my_method
end
end
describe MyController do
it 'calls my method' do
expect(controller).to receive(:my_method)
get :index
end
end

Related

Controll spec ActionController::UrlGenerationError

I've got routes setup so that they work as expected in my controllers; I can use both room_path and rooms_path as expected.
However when I try to use the same routes in a controller spec for some reason then I get an error:
ActionController::UrlGenerationError:
No route matches {:action=>"/1", :controller=>"rooms"}
My routes.rb file:
root "rooms#index"
resources :rooms, :path => '/', only: [:index, :create, :show] do
resources :connections, only: [:create,:destroy]
end
And if I rake routes:
room_connections POST /:room_id/connections(.:format) connections#create
room_connection DELETE /:room_id/connections/:id(.:format) connections#destroy
rooms GET / rooms#index
POST / rooms#create
room GET /:id(.:format) rooms#show
However my test fails:
describe "GET room_path(room)" do
it "renders show" do
#room = Room.create
get room_path(#room)
expect(response.status).to eq(200)
expect(response).to render_template(:show)
end
end
While my controllers can use the same route helpers without issue:
class RoomsController < ApplicationController
def index
end
def create
#room = Room.create
redirect_to room_path(#room)
end
def show
#room = Room.find(params[:id])
end
end
I'm not sure why in my tests it seems to go looking for a "/1" action rather than rooms#show like I would expect.
Update
So continuing to play this I've been able to get the test green by changing to the following:
describe "GET room_path(room)" do
it "renders show" do
#room = Room.create
get :show, params: { id: #room.id }
expect(response.status).to eq(200)
expect(response).to render_template(:show)
end
end
I would still love to understand why my helpers aren't working though. Is this to be expected? Manually writing the Parameters hash is kind of a PITA.
I don't know what version of Rails and RSpec you're on. This works for me on Rails 4.2 and Rspec 3.4.4:
describe "my neat test description", type: :routing do
it "can use path helper" do
puts whatever_path
end
end
The type: :routing pulls in the path and url helpers.
I believe the reason you don't have that by default is that rspec is replicating a lot of the environment for the different types of tests. For controller tests, it's pulling in the various path and url helpers because generally speaking they're used there often enough to be worth pulling in. In model tests, for example, they aren't used there often so they aren't pulled in by default.
These helpers live on the app object.

Rails RSPEC controller testing to count # of users on INDEX page

Working on controller testing and wanted to test that when I go to the index page, I should see the total number of users created and that should equal all the users that were in fact created. Can't get it to work and no errors are coming up, it just freezes and I have to press control c to exit.
describe "GET #index" do
it "show a list of all users" do
total = User.all.count
get :index
expect(response).to eq total
end
rspec controller tests don't render views by default, testing success might be better start
describe "GET #index" do
it "show a list of all users" do
get :index
expect(response).to be_success
end
end
If you really want to check rendering
describe "GET #index" do
render_views
it "show a list of all users" do
total = User.all.count
get :index
expect(response).to contain total.to_s
# OR
expect(response.body).to match total.to_s
end
end
see: https://www.relishapp.com/rspec/rspec-rails/v/2-2/docs/controller-specs/render-views
If you want check displaying of some information on page, it will be better to write integrations test using Capybara.
Purpose of controller tests is to check incoming parameters, variables initialized in controller and controller response (rendering views or redirecting...).
About your question - if you have next controller:
class UsersController < ApplicationController
def index
#users = User.all
end
end
you can write next controller test:
describe UsersController do
it "GET #index show a list of all users" do
User.create(email: 'aaa#gmail.com', name: 'Tim')
User.create(email: 'bbb#gmail.com', name: 'Tom')
get :index
expect(assigns[:users].size).to eq 2
end
end

How to create a route for testing purposes?

I'm writing tests with rspec for my application controller in my rails app (written in Rails 4) and I'm running into a problem where it doesn't recognize the route for the HTTP request I'm sending. I know there's a way to do this using MyApp::Application.routes but I'm not able to get it working.
#application_controller_spec.rb
require 'spec_helper'
class TestController < ApplicationController
def index; end
end
describe TestController do
before(:each) do
#first_user = FactoryGirl.create(:user)
# this is to ensure that all before_filters are run
controller.stub(:first_time_user)
controller.stub(:current_user)
end
describe 'first_time_user' do
before(:each) do
controller.unstub(:first_time_user)
end
context 'is in db' do
before(:each) do
#user = FactoryGirl.create(:user)
controller.stub(:current_user).and_return(#user)
end
it 'should not redirect' do
get :index
response.should_not be_redirect
end
end
context 'is not in db' do
context 'session[:cas_user] does not exist' do
it 'should return nil' do
get :index
expect(assigns(:current_user)).to eq(nil)
end
end
it "should redirect_to new_user_path" do
controller.stub(:current_user, redirect: true).and_return(nil)
get :index
response.should be_redirect
end
end
end
The error I'm getting right now is
No route matches {:action=>"index", :controller=>"test"}
I would add the test#index route to config/routes.rb, but it doesn't recognize the Test Controller, so I want to do something like
MyApp::Application.routes.append do
controller :test do
get 'test/index' => :index
end
end
but I'm not sure where to add this or if this even works in rspec. Any help would be great!
If you are trying to test your ApplicationController, see this RSpec documentation about it. You will need to define methods like index inside the test, but it works well.

Trace Rspec's get request routing in controller action spec

I am trying to test a simple controller action in a moduled controller. However, my get :index request returns a 404, instead of a 200 response. Is there a way to trace the routing of this get request?
require "spec_helper"
describe Admin::WidgetsController do
describe "GET index" do
it "has a 200 status code" do
get :index
response.code.should eq("200")
end
end
end
The controller looks like as you would expect:
class Admin::WidgetsController < Admin::ApplicationController
respond_to :html, :xml, :json
def index
respond_with(#content = "content")
end
end
Sounds like something is wrong with your routing. On the console you can run this to see what routes are available to your app:
$> rake routes
I'm pretty sure the following, when it fails, will show you what it's being redirected to
describe Admin::WidgetsController do
describe "GET index" do
it "has a 200 status code" do
get :index
response.should redirect_to(:action => 'other_action')
end
end
end
You can check out these links for more info:
http://guides.rubyonrails.org/routing.html
http://old.rspec.info/rails/writing/controllers.html

rspec stub current_company

I have a Rails 3 project in which I want to store the current company selected in a session variable.
I'm working with the staff controller spec and would like to stub out current_company for now as I'm isolating my spec example for the staff new controller action.
it "should call current_company" do
company = mock_model(Company, :id => "1")
controller.should_receive(:current_company).and_return(company)
get :new
end
Here is my new action for the staff controller
def new
#staff = Staff.new
#staff.company_id = current_company.id
end
I keep getting error
Failure/Error: get :new
NameError:
undefined local variable or method `current_company' for #<StaffsController:0x000000028d6ad8>
I've also tried just stubbing it out instead of using should_receive
controller.stub!(:current_company).and_return(company)
I get the same error.
Your code looks fine to me, it should work. There must be some other problem we are not seeing. I notice the controller name is "StaffsController" -- is that correct? Double-check the names of the controller and the corresponding spec -- they should be the same.
I think it was bombing out on the 'should be successful' example/test, so I've put my stubbing in a before block.
require 'spec_helper'
describe StaffsController do
describe "GET 'new'" do
let(:staff) { mock_model(Staff, :company_id= => nil)}
let(:company) { mock_model(Company, :id => 1)}
before do
Staff.stub!(:new).and_return(staff)
controller.stub!(:current_company).and_return(company)
end
it "should be successful" do
get :new
response.should be_success
end
it "should call current_company" do
controller.should_receive(:current_company).and_return(company)
get :new
end
end
end
This works for:
class StaffsController < ApplicationController
def new
#staff = Staff.new
current_company.id
end
end

Resources