Rspec controller testing - unexpected errors/not sure how to handle it - ruby-on-rails

I use this tests for FacultiesController:
describe FacultiesController do
describe "GET show" do
it "responds successfully with an HTTP 200 status code" do
get :show
expect(response).to be_success
expect(response).to have_http_status(200)
end
end
describe "GET index" do
it "responds successfully with an HTTP 200 status code" do
get :index
expect(response).to be_success
expect(response).to have_http_status(200)
end
end
end
Output:
Failures:
1) FacultiesController GET show responds successfully with an HTTP 200 status code
Failure/Error: get :show
ActionController::UrlGenerationError:
No route matches {:action=>"show", :controller=>"faculties"}
# ./spec/controllers/faculties_controller_spec.rb:5:in `block (3 levels) in <top (required)>'
2) FacultiesController GET index responds successfully with an HTTP 200 status code
Failure/Error: #faculties=Faculty.where(:university_id => #university.id).all
NoMethodError:
undefined method `id' for nil:NilClass
# ./app/controllers/faculties_controller.rb:5:in `index'
# ./spec/controllers/faculties_controller_spec.rb:13:in `block (3 levels) in <top (required)>'
1) When I run rake routes I see
faculty GET /faculties/:shortcut(.:format) faculties#show
So, I suppose I need to specify shortcut with this GET. But how?
I tried add :shortcut=>"test", but this didn't work, also tried params: {:shortcut=>"test"}. Nothing works until I just write "GET faculties/test" and also remove line get :show.
Is that as it should to be?
2)
Failure/Error: #faculties=Faculty.where(:university_id => #university.id).all
NoMethodError:
undefined method `id' for nil:NilClass
First of all, it, again, work just fine in browser testing. I use module which helps controller to define #university by subdomain.
How can I pass this to test? Should I just provide some option to get index? By the way, it is all defined in my factories.rb file, as subdomain string is stored in db.
I though may be I should invoke factories new instance in this spec before describe?
university = create(:university)
faculty = create(:faculty)
Thanks in advance.

1) You should create a new faculty and pass it's id
describe "GET show" do
let(:faculty) {FactoryGirl.create(:faculty)}
it "responds successfully with an HTTP 200 status code" do
get :show, :id => faculty.id #or get :show, :shortcut => faculty.id
expect(response).to be_success
expect(response).to have_http_status(200)
end
end
2) You should create faculty & university & pass it as params
describe "GET index" do
let(:university) {FactoryGirl.create(:university)}
before(:each) do
FactoryGirl.create(:faculty, :university_id => university.id)
end
it "responds successfully with an HTTP 200 status code" do
get :index, university_id => university.id
expect(response).to be_success
expect(response).to have_http_status(200)
end
end
Please provide index route if it wouldn't work

Related

Rails rspec wrong expected result

I'm having problem with using rspec in ruby-on-rails app
my users_spec.rb
require 'rails_helper'
RSpec.describe 'Get /', type: :request do
it 'test / get method' do
expect(response).to have_http_status(:success)
expect(response).to include('index')
end
end
my routes.rb
Rails.application.routes.draw do
get "/" => "users#index"
end
my users_controller.rb
class UsersController < ApplicationController
def index
end
end
my index.erb
<h1>index</h1>
when I run "bundle exec rspec". , I get error like this
.F
Failures:
1) Get / test / get method
Failure/Error: expect(response).to have_http_status(:success)
expected the response to have a success status code (2xx) but it was
# ./spec/requests/users_spec.rb:5:in `block (2 levels) in <main>'
Finished in 0.11204 seconds (files took 5.18 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/requests/users_spec.rb:4 # Get / test / get method
and I can't catch where is wrong..
Your spec is not correct. Your spec is not actually calling the endpoint. You need to add a get "/" call, that will actually call the endpoint
require 'rails_helper'
RSpec.describe 'Get /', type: :request do
it 'test / get method' do
get "/" #this is the missing line; this actually makes the request
expect(response).to have_http_status(:success)
expect(response).to include('index')
end
end

Rspec for conditional code if-else?

I am new to RSpec but here I am trying to create tests based on this code and I am keep on getting this error. Any suggestions?
CODE:
serialization_scope nil
before_action :set_list, only: [:show, :destroy, :update]
before_action :verify_user, only: :show
def create
#list = current_user.lists.build(list_params)
if #list.save
render json: {message: ['Success']}, status: 200
else
render json: {errors:[#list.errors.full_messages]}, status: 400
end
end
Here is the RSpec file that I started :
require "rails_helper"
RSpec.describe V1::ListsController, :type => :controller do
describe "POST create" do
it "returns HTTP status" do
expect(post :create).to change(#list, :count).by(+1)
expect(response).to have_http_status :success #200
end
end
describe 'GET status if its not created' do
it "return HTTP status - reports BAD REQUEST (HTTP status 400)" do
expect(response.status).to eq 400
end
end
end
And the error that I got is :
Failures:
1) V1::ListsController GET status if its created returns HTTP status
Failure/Error: expect(post :create).to change(#list, :count).by(+1)
expected #count to have changed by 1, but was not given a block
# ./spec/controllers/lists_controller_spec.rb:8:in `block (3 levels) in <top (required)>'
2) GET status if its not created return HTTP status - reports BAD REQUEST (HTTP status 400)
Failure/Error: expect(response.status).to eq 400
expected: 400
got: 200
(compared using ==)
Try this code.
require 'rails_helper'
RSpec.describe V1::ListsController, type: :request do
describe 'valid request' do
it 'returns HTTP status' do
post '/list', params: { list: { list_name: 'xyz' } }
expect(response.status).to eq 201
end
end
describe 'invalid request' do
it "should return unauthorized" do
post '/list'
assert_response :unauthorized
end
end
end
In params you need to pass your list_params.
Spec would look like:
describe "POST create" do
context 'valid request' do
it 'should increase #list item' do
expect { post :create }.to change(List, :count).by(1)
end
it "returns HTTP status" do
post :create
expect(response).to have_http_status :success #200
end
end
context 'invalid request' do
it "return HTTP status - reports BAD REQUEST (HTTP status 400)" do
get :create
expect(response.status).to eq 400
end
end
end
Cheers!
You can test an object not being created by intentionally causing some of its validations to fail e.g. you can pass a mandatory attribute as nil from the RSpec.
Sample request: post :create, { title: nil }.
But as per your RSpec code, it seems there are no validations on List model. So, lets try to stub save and return false for this particular test.
describe 'GET status if its not created' do
# Assuming your model name is `List`
before { allow_any_instance_of(List).to receive(:save) { false } }
it "return HTTP status - reports BAD REQUEST (HTTP status 400)" do
post :create
expect(response.status).to eq 400
end
end
Please post your model for list and i can update the answer with more appropriate test.
Ishika, let me see if I can help you :)
RSpec official documentation recommends you to use request specs instead of controller specs. That is recommended because Rails 5 deprecated some methods used on controller testings. You can read more about this here at RSpec blog
ps.: You can use controller tests so far, but it can be deprecated in a future major version of RSpec.
There are some notes I left after the code, please read them also.
I would write a request spec like this:
# spec/requests/v1/lists_controller_create_spec.rb
require "rails_helper"
RSpec.describe V1::ListsController do
describe 'success' do
it 'returns ok and creates a list', :aggregate_failures do # :aggregate_failures is available only for RSpec 3.3+
expect do
post '/list', title: 'foo' # This will also test your route, avoiding routing specs to be necessary
end.to change { List.count }.from(0).to(1)
expect(response).to have_http_status(:ok)
end
end
describe 'bad request' do
before do
# This is needed because your controller is not validating the object, but look at my
# comment below (out of the code), to think about this behavior, please.
allow_any_instance_of(List).to receive(:save).and_return(false)
end
it 'returns a bad request and does not create a list' do
expect do
post '/list', title: 'foo' # This will also test your route, avoiding routing specs to be necessary
end.not_to change { List.count }
expect(response).to have_http_status(:bad_request)
end
end
end
Notes:
I suggested using more than 1 expectation by example, that is ok in this spec because they are simple and because I'm using :aggregate_failures option. With this option, if the first expectation fails, the next expectations will also be executed, considering that in this case, the following expectations does not depend on the first one, it is ok to use more than 1 expectation for the example.Reference
You are returning a bad request if the object is not saved, but you are not validating it. If your model has validations that will validate the object there, please adjust the specs to fail the save (instead of using the mock I used) and consider rendering an error message in the response
If you think that making the post inside a expect block, you can do different: Store the count of Lists in a variable before making the post and after the post you test if the variable has changed or not, maybe you think it will be more clear and it will do exactly the same thing in the background.

Rspec Error.(Couldn't find Asking with)

This below is spec/controllers/askings_controller_spec.rb.
require 'rails_helper'
RSpec.describe AskingsController, type: :controller do
describe 'Get #show' do
before do
#asking=create(:asking)
get :show , id: #asking.id
end
it 'should render show' do
expect(response).to render_template(:show)
end
it 'should Undertaking new' do
expect(assigns(:undertaking)).to be_a_new(Undertaking)
end
it 'should response status 200' do
expect(response.status).to eq(200)
end
end
end
And rspec result is below.
1) AskingsController Get #show should render show
Failure/Error: #asking=Asking.find(id: params[:id])
ActiveRecord::RecordNotFound:
Couldn't find Asking with 'id'={:id=>"2"}
2) AskingsController Get #show should Undertaking new
Failure/Error: #asking=Asking.find(id: params[:id])
ActiveRecord::RecordNotFound:
Couldn't find Asking with 'id'={:id=>"2"}
3) AskingsController Get #show should response status 200
Failure/Error: #asking=create(:asking)
ActiveRecord::RecordInvalid:
mailaddress exists.(<- I translate Japanese error.)
Why do I have this eeror?
I think that how to use 'create' is incorrect.
Please tell me the solution.

Rails/rspec, integration test fail with "no route matches" but route does exist

I am doing some simple integration testing. I want to test the sold_items action in the users controller. I have confirmed that the route exists and returns json by accessing it from the browser. However, rspec is telling me that the route doesn't exist. PLease see below, the spec, the error, and my route.rb. Thanks!
spec:
require 'rails_helper'
RSpec.describe UsersController, type: :controller do
describe "GET #items" do
it "returns http success" do
user = FactoryGirl.create(:user)
get "users/#{user.id}/sold_items"
expect(response).to have_http_status(:success)
end
end
end
fail message:
1) UsersController GET #items returns http success
Failure/Error: get "users/#{user.id}/sold_items"
ActionController::UrlGenerationError:
No route matches {:action=>"users/10/sold_items", :controller=>"users"}
# ./spec/controllers/users_controller_spec.rb:8:in `block (3 levels) in <top (required)>'
routes.rb
Rails.application.routes.draw do
resources :users
resources :items
get "users/:id/sold_items" => "users#sold_items"
EDIT
spec
require 'rails_helper'
RSpec.describe UsersController, type: :controller do
describe "GET #items" do
it "returns http success" do
user = FactoryGirl.create(:user)
get "users/:id/sold_items", id: user.id
expect(response).to have_http_status(:success)
end
end
end
failure message
2) UsersController GET #items returns http success
Failure/Error: get "users/:id/sold_items", id: user.id
ActionController::UrlGenerationError:
No route matches {:action=>"users/:id/sold_items", :controller=>"users", :id=>"12"}
# ./spec/controllers/users_controller_spec.rb:8:in `block (3 levels) in <top (required)>'
EDIT.2 MarvC second suggestion
require 'rails_helper'
RSpec.describe UsersController, type: [:request, :controller] do
describe "GET #items" do
it "returns http success" do
user = FactoryGirl.create(:user)
get "/users/:id/sold_items", id: user.id
expect(response).to have_http_status(:success)
end
end
end
failure
looks like the user.id isn't being passed in properly here
2) UsersController GET #items returns http success
Failure/Error: #sold_items = User.find(params[:id]).seller_items.sold
ActiveRecord::RecordNotFound:
Couldn't find User with 'id'=:id
# ./app/controllers/users_controller.rb:6:in `sold_items'
# ./spec/controllers/users_controller_spec.rb:8:in `block (3 levels) in <top (required)>'
One Solution:
when I use string interpolation to pass user.id into the url it works...
require 'rails_helper'
RSpec.describe UsersController, type: [:request, :controller] do
describe "GET #items" do
it "returns http success" do
user = FactoryGirl.create(:user)
get "users/#{user.id}/sold_items", id: user.id
expect(response).to have_http_status(:success)
end
end
end
It seems your syntax is wrong.
change:
get "users/#{user.id}/sold_items"
To:
rails 4
get "users/:id/sold_items", id: user.id
rails 5
get "users/:id/sold_items", params: { id: user.id }

No route matches using rspec on a get request

What am I forgetting?
routes:
get "/comingsoon" => "visitors#comingsoon"
resources :visitors
controller:
class VisitorsController < ApplicationController
def comingsoon
#new_visitor = Visitor.new
end
end
spec:
require 'spec_helper'
describe VisitorsController do
describe "GET /comingsoon" do
it "should be happy" do
get "/comingsoon"
response.should be_success
end
end
end
And here's the result:
✗ rspec spec/controllers/visitors_controller_spec.rb
F
Failures:
1) VisitorsController GET /comingsoon should be valid
Failure/Error: get "/comingsoon"
ActionController::RoutingError:
No route matches {:controller=>"visitors", :action=>"/comingsoon"}
# ./spec/controllers/visitors_controller_spec.rb:7:in `block (3 levels) in <top (required)>'
Finished in 0.14226 seconds
1 example, 1 failure
Failed examples:
rspec ./spec/controllers/visitors_controller_spec.rb:6 # VisitorsController GET /comingsoon should be valid
What am I forgetting?
In your spec file replace get "/comingsoon"
with get "comingsoon"
When you spec a controller with rspec the operand of the http verb (get, post, put, delete) is an action of the controller rather than a url.
Possibly daft suggestion, but you have a view right? Otherwise you have to tell your controller to render something.

Resources