I'm trying to write a simple RSpec test for "UsersController" that tests the index method.
The code for the controller index method looks as follows:
# GET /users
# GET /users.json
def index
#users = User.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #users }
end
end
I am trying to test that the "all" method is called and that the index view is rendered. Here is my Rspec code for that:
require 'spec_helper'
describe UsersController do
describe 'get index', :type => :controller do
before :each do
#fake_users = [double('user1'), double('user2')]
end
it 'should call the model method that retrieves all Users' do
User.should_receive(:all).once.and_return(#fake_users)
get :index
end
describe 'after valid search' do
before :each do
User.stub(:all).and_return(#fake_users)
get :index
end
it 'should select the index template for rendering' do
response.should render_template('index')
end
it 'should make the users results available to that template' do
assigns(:users).should == #fake_users
end
end
end
end
However, this fails the "get index" test with the following message:
Failure/Error: User.should_receive(:all).once.and_return(#fake_users)
(<User(id: integer, firstname: string, lastname: string, username: string, email: string, password_digest: string, created_at: datetime, updated_at: datetime) (class)>).all(any args)
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/controllers/users_controller_spec.rb:9:in `block (3 levels) in <top (required)>'
Can anyone shed any light on what I'm doing wrong?
The problem was caused by the UsersController requiring a log in first. So the controller looked like:
class UsersController < AuthenticatedController
So the path wasn't accessible to RSpec.
Related
I'm following this tutorial for a rails API but it is a little outdated and some things don't seem to work with newer versions of rails. I'm having a hard time with the user controller specs:
user_controller_spec.rb
require 'rails_helper'
RSpec.describe Api::V1::UsersController, type: :controller do
describe "GET #show" do
before(:each) do
#user = FactoryGirl.create :user
get :show, params: {id: #user.id}
end
it "returns the information about a reporter on a hash" do
user_response = JSON.parse(response.body, symbolize_name: true)
expect(user_response[:email]).to eql #user.email
end
it { expect(response).to have_http_status(200) }
end
end
user_controller.rb
class Api::V1::UsersController < ApplicationController
def show
render json: User.find(params[:id])
end
end
user.rb factory
FactoryGirl.define do
factory :user do
email { FFaker::Internet.email }
password "12345678"
password_confirmation "12345678"
end
end
But, this isn't working, the email doesn't seem to match. Any ideas what could be wrong?
Failures:
1) Api::V1::UsersController GET #show returns the information about a reporter on a hash
Failure/Error: expect(user_response[:email]).to eql #user.email
expected: "mitzie_nikolaus#rice.com"
got: nil
(compared using eql?)
# ./spec/controllers/api/v1/users_controller_spec.rb:12:in `block (3 levels) in <top (required)>'
The code is correct, but you've made a typo in using the symbolize_names option for JSON.parse.
I assume, that because you do not copy-paste examples, but type it by your own, which is great, because it's better for learning.
To fix the test just correct this line (change symbolize_name to symbolize_names):
user_response = JSON.parse(response.body, symbolize_names: true)
I have this rspec code:
let(:valid_attributes) {
{name: "Sample Product"}
}
describe "#index" do
it "should give a collection of products" do
product = Product.create! valid_attributes
get :index, :format => :json
expect(response.status).to eq(200)
expect(response).to render_template("api/products/index")
expect(assigns(:products)).to eq([product])
end
end
And it controller:
def index
#products = Product.all
end
But the controller code still doesn't satisfy the spec. What is wrong here.
Here is the failure message:
Failures:
1) Api::ProductsController#index should give a collection of
products
Failure/Error: expect(assigns(:products)).to eq([product])
expected: [#<Product id: 3, name: "Sample Product", created_at: "2016-06-15 05:10:50", updated_at: "2016-06-15 05:10:50">]
got: nil
(compared using ==)
# ./spec/controllers/api/products_controller_spec.rb:53:in `block (3 levels) in <top (required)>'
Finished in 0.05106 seconds (files took 1.89 seconds to load) 1
example, 1 failure
You have:
get :index, :format => :json
I assume you should have:
get :index, :format => :html
By default, rails returns html, and you didn't specify otherwise in your index action.
assign checks that an instance variable was set
and products should be created in database (use let!(with bang) for it), then:
in your controller:
def index
#products = Product.all # `#products` should be present
end
in rspec:
let(:valid_attributes) {{ name: 'Sample Product' }}
let!(:product) { Product.create!(valid_attributes) } # use `!` here
describe "#index" do
before { get :index, format :json }
it 'should give a collection of products' do
expect(assigns(:products)).to eq([product])
end
end
I seem to be stuck. I am trying to shore up some rspec testing and want to make sure the the correct before_filter methods are getting called for controllers. However, I am getting feedback saying the method never gets called.
The error:
Failure/Error: expect(controller).to receive(:authorize)
(#<UsersController:0x007fca2fd27110>).authorize(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
The spec:
require "rails_helper"
RSpec.describe UsersController, :type => :controller do
let(:school){ FactoryGirl.create :school }
let(:user){ FactoryGirl.create :teacher}
before(:each){
allow(controller).to receive(:current_user).and_return(user)
school.teachers << user
}
context "Get #show" do
before(:each){ get :show, school_id: school.id, id: user.id }
it "responds successfully with an HTTP 200 status code" do
expect(controller).to receive(:authorize)
expect(response).to have_http_status(200)
end
it "renders the show template" do
expect(response).to render_template("show")
end
end
end
The controller:
class UsersController < ApplicationController
before_filter :authorize
def show
#user = User.find_by_id params[:id]
#school = #user.school
#coordinators = #school.coordinators
#teachers = #school.teachers
#speducators = #school.speducators
#students = #school.students
end
end
Manual testing shows that before is being called, and when I put a p in the authorize method it is called when I run the test, any thoughts on where the test is going wrong?
You must set method expectation before actual call, so your test should look like:
context "Get #show" do
subject { get :show, school_id: school.id, id: user.id }
it "calls +authorize+ befor action" do
expect(controller).to receive(:authorize)
subject
end
end
Check the documentation https://github.com/rspec/rspec-mocks#message-expectations
i have used devise in rspec testing. this is my test
describe BooksController do
before(:all) do
#user = FactoryGirl.create(:user)
end
describe "GET index" do
it "shows list of current user books" do
sign_in #user
book = #user.books.create!(:title => "user")
get :index, {}
assigns(:books).should eq(#user.books)
end
end
describe "GET show" do
it "assigns the requested book as #book" do
sign_in #user
book = #user.books.create!(:title => "user")
visit_count = book.visits.to_i
get :show, {:id => book.to_param}
assigns(:book).should eq(book)
book = Book.find(book.id)
visit_count.should_not eq(book.visits)
end
end
describe "GET new" do
it "assigns a new book as #book" do
sign_in #user
get :new, {}
assigns(:book).should be_a_new(Book)
end
end
end
factory
FactoryGirl.define do
factory :user do
sequence(:email) { |n| "foo#{n}#example.com" }
password '12345678'
password_confirmation '12345678'
confirmed_at Time.now
end
end
book controller
class BooksController < ApplicationController
before_action :authenticate_user!, only: [:index, :edit, :update, :destroy, :new, :my_books, :add_wish_list]
# GET /books
# GET /books.json
def index
#books = current_user.books
end
# GET /books/1
# GET /books/1.json
def show
#book = Book.find(params[:id])
#book.book_visit_count
if(session["warden.user.user.key"].present?)
#book.book_visit_user(session["warden.user.user.key"][0][0])
end
end
# GET /books/new
def new
#book = Book.new
end
end
error
Failure/Error: assigns(:book).should be_a_new(Book)
expected nil to be a new Book(id: integer, title: string, author: string, isbn_10: string, isbn_13: string, edition: integer, print: integer, publication_year: integer, publication_month: string, condition: string, value: integer, status: boolean, stage: integer, description: text, visits: integer, user_id: integer, prefered_place: string, prefered_time: string, created_at: datetime, updated_at: datetime, rating: integer, image: string, publisher: string, goodreads_id: string)
# ./spec/controllers/books_controller_spec.rb:66:in `block (3 levels) in <top (required)>'
the problem is the third test "get new" fails when i run the test as a whole but passes when i run it individually. and also if i remove the before_authenticate! in controller then all test passes.
Again if i commented out the "assigns" in first two describe blocks then all tests pass again.
i am using rails 4.0.2 and rspec 2.14.7 , devise 3.2.2
The only thing I can figure is that your authenticate_user method is failing for users that have previously been authenticated. It's not affecting show because you don't have :show listed in your before_action. You could test this theory by requiring authentication for show as well and seeing if your second example starts failing for before(:all) as well.
I am attempting to create an API with Rails using BDD with RSpec.
Rails version is 3.1.1, Ruby version is 1.9.2, Devise version is 1.5.3, and rspec version is 2.7.0. I am relatively new to Rails and very new to RSpec.
I have defined a simple RSpec as follows to test a FormsController with essentially no logic.
describe FormsController, " handling GET /forms" do
include Devise::TestHelpers
render_views
before do
user = Factory.create(:user) # Handle Devise authentication
user.confirm!
sign_in user
#form = mock_model(Form)
Form.stub!(:all).and_return([ #form ])
end
it "gets successfully" do
get :index, :format => :json
response.should be_success
end
it "finds all forms" do
Form.should_receive(:all).and_return([#form])
get :index, :format => :json
Rails.logger.info "*** response.body="+response.body
end
end
Form controller code is very simple currently.
class FormsController < ApplicationController
before_filter :authenticate_user!
# GET /forms
# GET /forms.json
def index
#forms = Form.find_all_by_owner_id(current_user.id)
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #forms }
end
end
end
When I run the spec, "finds all forms" always fails with
Failure/Error: Form.should_receive(:all).and_return([#form])
(<Form(id: integer, title: string, owner_id: integer, created_at: datetime, updated_at: datetime) (class)>).all(any args)
expected: 1 time
received: 0 times
The output from log/test.log shows:
*** response.body=[]
Why? I feel that the problem stems from Form.stub!(:all).and_return([ #form ]), but I am not sure how to debug.
Thanks in advance.
It would help to post your controller code (that is being tested). The error says that the declaration Form.should_receive(:all).and_return([#form]) has not been satisfied. The declaration says you should have code like this in your controller's action: Form.all.
find_all_by_owner_id is not the same as Form.all. find_all_by_owner_id ends up doing
Form.where(...).all
which doesn't match the expectations you've set. In your particular case I'd tell should_receive that I'm expecting a call to find_all_by_owner_id rather than all.
After much more trial and error, the following solution worked for me.
I migrated from mocking the Form model to using Factory Girl to create the full model
I then updated the test to use to_json to compare the response against the model.
The spec is as follows.
describe FormsController, " handling GET /forms" do
include Devise::TestHelpers
render_views
before do
user = Factory.create(:user) # Handle Devise authentication
user.confirm!
sign_in user
#form1 = Factory.create(:form)
end
it "gets successfully" do
get :index, :format => :json
response.should be_success
end
it "finds all forms" do
get :index, :format => :json
response.body.should == [ #form1 ].to_json
Rails.logger.info "*** response.body="+response.body
end
end