rspec BDD tests - trying to test index controller method - ruby-on-rails

I have a rails app with a cars model that takes the attribute of 'model'. I'm just trying to run a test to see if the index method in the cars controller will display all the cars.
I'm not sure how to go about this. The test currently passes, but it shouldn't as I currently have an empty index method. How could I test this and what do I need to add. I've tried to read documentation with no luck. thanks
require 'rails_helper'
RSpec.describe CarsController, type: :controller do
context "test" do
it "displays all cars" do
get :index
end
end
end

with the current test file you have posted, your test should pass because you are doing a request to the index action and as long as there is an index action the test will pass.
require 'rails_helper'
RSpec.describe CarsController, type: :controller do
context "test" do
it "displays all cars" do
get :index
end
end
end
However I see some people have recommended to use assigns, when using assigns to test you will need to assigns all the cars to an instance variable e.g. #cars = Cars.all or whatever records you need and also you should have your test data setup properly.
In your test you should also have some sort of list of cars to test against, e.g assuming you are using FactoryGirl/Bot:
let(:cars) { create_list :car, 3 }
You test file should than look something like this:
require 'rails_helper'
RSpec.describe CarsController, type: :controller do
let(:cars) { create_list :car, 3 }
context "test" do
it "displays all cars" do
get :index
expect(assigns(:cars)).to eq cars
end
end
end
Hope my answer helps.

You're looking to use assigns in your controller specs. Since you don't seem to have any code yet, the fastest way to show you an example is to just run rails g scaffold car. Assuming you have rspec-rails in your Gemfile, you should get something like this in spec/controllers/cars_controller_spec.rb:
require 'rails_helper'
# This spec was generated by rspec-rails when you ran the scaffold generator.
# It demonstrates how one might use RSpec to specify the controller code that
# was generated by Rails when you ran the scaffold generator.
#
# It assumes that the implementation code is generated by the rails scaffold
# generator. If you are using any extension libraries to generate different
# controller code, this generated spec may or may not pass.
#
# It only uses APIs available in rails and/or rspec-rails. There are a number
# of tools you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
#
# Compared to earlier versions of this generator, there is very limited use of
# stubs and message expectations in this spec. Stubs are only used when there
# is no simpler way to get a handle on the object needed for the example.
# Message expectations are only used when there is no simpler way to specify
# that an instance is receiving a specific message.
RSpec.describe CarsController, type: :controller do
# This should return the minimal set of attributes required to create a valid
# Car. As you add validations to Car, be sure to
# adjust the attributes here as well.
let(:valid_attributes) {
skip("Add a hash of attributes valid for your model")
}
let(:invalid_attributes) {
skip("Add a hash of attributes invalid for your model")
}
# This should return the minimal set of values that should be in the session
# in order to pass any filters (e.g. authentication) defined in
# CarsController. Be sure to keep this updated too.
let(:valid_session) { {} }
describe "GET #index" do
it "assigns all cars as #cars" do
car = Car.create! valid_attributes
get :index, params: {}, session: valid_session
expect(assigns(:cars)).to eq([car])
end
end
describe "GET #show" do
it "assigns the requested car as #car" do
car = Car.create! valid_attributes
get :show, params: {id: car.to_param}, session: valid_session
expect(assigns(:car)).to eq(car)
end
end
describe "GET #new" do
it "assigns a new car as #car" do
get :new, params: {}, session: valid_session
expect(assigns(:car)).to be_a_new(Car)
end
end
describe "GET #edit" do
it "assigns the requested car as #car" do
car = Car.create! valid_attributes
get :edit, params: {id: car.to_param}, session: valid_session
expect(assigns(:car)).to eq(car)
end
end
describe "POST #create" do
context "with valid params" do
it "creates a new Car" do
expect {
post :create, params: {car: valid_attributes}, session: valid_session
}.to change(Car, :count).by(1)
end
it "assigns a newly created car as #car" do
post :create, params: {car: valid_attributes}, session: valid_session
expect(assigns(:car)).to be_a(Car)
expect(assigns(:car)).to be_persisted
end
it "redirects to the created car" do
post :create, params: {car: valid_attributes}, session: valid_session
expect(response).to redirect_to(Car.last)
end
end
context "with invalid params" do
it "assigns a newly created but unsaved car as #car" do
post :create, params: {car: invalid_attributes}, session: valid_session
expect(assigns(:car)).to be_a_new(Car)
end
it "re-renders the 'new' template" do
post :create, params: {car: invalid_attributes}, session: valid_session
expect(response).to render_template("new")
end
end
end
describe "PUT #update" do
context "with valid params" do
let(:new_attributes) {
skip("Add a hash of attributes valid for your model")
}
it "updates the requested car" do
car = Car.create! valid_attributes
put :update, params: {id: car.to_param, car: new_attributes}, session: valid_session
car.reload
skip("Add assertions for updated state")
end
it "assigns the requested car as #car" do
car = Car.create! valid_attributes
put :update, params: {id: car.to_param, car: valid_attributes}, session: valid_session
expect(assigns(:car)).to eq(car)
end
it "redirects to the car" do
car = Car.create! valid_attributes
put :update, params: {id: car.to_param, car: valid_attributes}, session: valid_session
expect(response).to redirect_to(car)
end
end
context "with invalid params" do
it "assigns the car as #car" do
car = Car.create! valid_attributes
put :update, params: {id: car.to_param, car: invalid_attributes}, session: valid_session
expect(assigns(:car)).to eq(car)
end
it "re-renders the 'edit' template" do
car = Car.create! valid_attributes
put :update, params: {id: car.to_param, car: invalid_attributes}, session: valid_session
expect(response).to render_template("edit")
end
end
end
describe "DELETE #destroy" do
it "destroys the requested car" do
car = Car.create! valid_attributes
expect {
delete :destroy, params: {id: car.to_param}, session: valid_session
}.to change(Car, :count).by(-1)
end
it "redirects to the cars list" do
car = Car.create! valid_attributes
delete :destroy, params: {id: car.to_param}, session: valid_session
expect(response).to redirect_to(cars_url)
end
end
end

Related

Rspec ActionController::UrlGenerationError Rails 4.2

I'm using Rails 4.2 rspec 3.5
after basic scaffold generation, i was trying to run rspec, but constantly getting this errors.
controller test
it "updates the requested" do
some_object = SomeObject.create! valid_attributes
put :update, params: {id: some_object.to_param, some_object: new_attributes}, session: valid_session
some_object.reload
skip("Add assertions for updated state")
end
gives this output
Failure/Error: put :update, params: {id: some_object.to_param, some_object: valid_attributes}, session: valid_session
ActionController::UrlGenerationError:
No route matches {:action=>"update"
And views test
require 'rails_helper'
RSpec.describe "some_objects/edit", type: :view do
before(:each) do
#some_object = assign(:some_object, SomeObject.create!(
:first_name => "MyString",
:last_name => "MyString"
))
end
it "renders the edit some_object form" do
render
assert_select "form[action=?][method=?]", some_object_path(#some_object), "post" do
assert_select "input#some_object_first_name[name=?]", "some_object[first_name]"
assert_select "input#some_object_last_name[name=?]", "some_object[last_name]"
end
end
end
gives this output:
Failure/Error: <%= form_for(#some_object) do |f| %>
ActionView::Template::Error:
undefined method `polymorphic_path' for #<#<Class:0x005606c04fff98>:0x005606c04ffb60>
This is generated code. I have changed only valid_attributes hash, and this seem to be some bug in rails/rspec. Do you have any solution
Problem is in passing params for :update. Simply remove params: and it will work:
it "updates the requested" do
some_object = SomeObject.create! valid_attributes
put :update, {id: some_object.to_param, some_object: new_attributes}, session: valid_session
some_object.reload
skip("Add assertions for updated state")
end

ActionController::UrlGenerationError, No route matches

I've read through every similar question I could find and still can't figure out my problem.
# routes.rb
Rails.application.routes.draw do
resources :lists, only: [:index, :show, :create, :update, :destroy] do
resources :items, except: [:new]
end
end
# items_controller.rb (excerpt)
class ItemsController < ApplicationController
...
def create
#list = List.find(params[:list_id])
...
end
...
end
# items_controller_spec.rb (excerpt)
RSpec.describe ItemsController, type: :controller do
...
let!(:list) { List.create(title: "New List title") }
let(:valid_item_attributes) {
{ title: "Some Item Title", complete: false, list_id: list.id }
}
let!(:item) { list.items.create(valid_item_attributes) }
describe "POST #create" do
context "with valid params" do
it "creates a new item" do
expect {
post :create, { item: valid_item_attributes, format: :json }
}.to change(Item, :count).by(1)
end
end
end
...
end
And the RSpec error:
1) ItemsController POST #create with valid params creates a new item
Failure/Error: post :create, { item: valid_item_attributes, format: :json }
ActionController::UrlGenerationError:
No route matches {:action=>"create", :controller=>"items", :format=>:json, :item=>{:title=>"Some Item Title", :complete=>false, :list_id=>1}}
The output from rake routes:
list_items GET /lists/:list_id/items(.:format) items#index
POST /lists/:list_id/items(.:format) items#create
edit_list_item GET /lists/:list_id/items/:id/edit(.:format) items#edit
list_item GET /lists/:list_id/items/:id(.:format) items#show
PATCH /lists/:list_id/items/:id(.:format) items#update
PUT /lists/:list_id/items/:id(.:format) items#update
DELETE /lists/:list_id/items/:id(.:format) items#destroy
I can successfully create a new item in an existing list via curl which tells me that the route is ok, I must be doing something wrong in my test.
curl -i -X POST -H "Content-Type:application/json" -H "X-User-Email:admin#example.com" -H "X-Auth-xxx" -d '{ "item": { "title": "new item", "complete": "false"} }' http://localhost:3000/lists/5/items
I am really confused. My routes are setup correctly. A ItemsController#create method definitely exists. The rest of the tests in items_controller_spec.rb pass without issue.
Am I missing something obvious?
Here are the fixes I had to make to my tests (items_controller_spec.rb). I was not passing the correct hash to post create:.
describe "POST #create" do
context "with valid params" do
it "creates a new item" do
expect {
post :create, { list_id: list.id, item: valid_item_attributes, format: :json }
}.to change(Item, :count).by(1)
end
it "assigns a newly created item as #item" do
post :create, { list_id: list.id, item: valid_item_attributes, format: :json }
expect(assigns(:item)).to be_a(Item)
expect(assigns(:item)).to be_persisted
end
end # "with valid params"
context "with invalid params" do
it "assigns a newly created but unsaved item as #item" do
post :create, { list_id: list.id, item: invalid_item_attributes, format: :json }
expect(assigns(:item)).to be_a_new(Item)
end
it "returns unprocessable_entity status" do
put :create, { list_id: list.id, item: invalid_item_attributes, format: :json }
expect(response.status).to eq(422)
end
end # "with invalid params"
end # "POST #create"
I've received the same error and fixed it in a different way. I'm using Rails ~> 5.0.7.
ROUTES
This is the output from running rake routes CONTROLLER=bills:
Prefix Verb URI Pattern Controller#Action
download_site_bill GET /sites/:site_id/bills/:id/download(.:format) bills#download
site_bills GET /sites/:site_id/bills(.:format) bills#index
POST /sites/:site_id/bills(.:format) bills#create
new_site_bill GET /sites/:site_id/bills/new(.:format) bills#new
edit_site_bill GET /sites/:site_id/bills/:id/edit(.:format) bills#edit
site_bill GET /sites/:site_id/bills/:id(.:format) bills#show
PATCH /sites/:site_id/bills/:id(.:format) bills#update
PUT /sites/:site_id/bills/:id(.:format) bills#update
DELETE /sites/:site_id/bills/:id(.:format) bills#destroy
bills GET /bills(/page/:page)(.:format) bills#index
download_bill GET /bills/:id/download(.:format) bills#download
GET /bills(.:format) bills#index
POST /bills(.:format) bills#create
new_bill GET /bills/new(.:format) bills#new
edit_bill GET /bills/:id/edit(.:format) bills#edit
bill GET /bills/:id(.:format) bills#show
PATCH /bills/:id(.:format) bills#update
PUT /bills/:id(.:format) bills#update
DELETE /bills/:id(.:format) bills#destroy
REFERENCE CODE
# controllers/admin/bills_controller.rb (excerpt)
module Admin
class BillsController < ApplicationController
...
def edit
authorize(#bill)
end
end
end
# spec/controllers/admin/bills_controller_spec.rb (excerpt)
require 'rails_helper'
RSpec.describe Admin::BillsController, type: :controller do
...
context 'as an AdminUser' do
login_admin_user
it 'loads the bill edit page' do
request.host = 'admin.example.com'
get :edit, { id: bill }
expect(response.status).to eq(200)
end
end
end
end
# Error message
2) Admin::BillsController GET bills/:id/edit as a User redirects to the home page
Failure/Error: get :edit
ActionController::UrlGenerationError:
No route matches {:action=>"edit", :controller=>"admin/bills"}
POTENTIAL SOLUTION
I tried variations of this solution. It may work for others, but I got this error:
Failure/Error: require 'admin/bills_controller'
LoadError:
cannot load such file -- admin/bills_controller
SOLUTION
I tried Case 2 in this github issues and it worked. The changes I made to my code were to add this params: { use_route: 'admins/bills/', id: bill.id }. Below is the context of this addition:
context 'as an AdminUser' do
login_admin_user
it 'loads the bill edit page' do
request.host = 'admin.example.com'
get :edit, params: { use_route: 'admins/bills/', id: bill.id }
expect(response.status).to eq(200)
end
end

Why are my tests telling me "param is missing or the value is empty:"?

Using Rails 4.2, rspec 2.14, rspec-rails 2.14, faker and factory-girls-rails gems
I have a model called Appointment that I'm running some tests on and everything passes except for the #create under the controller spec.
The error message I get is:
Failure/Error: post :create, FactoryGirl.attributes_for(:appointment)
ActionController::ParameterMissing:
param is missing or the value is empty: appointment
The Appointment model validates the presence of an association to an object called Service.
Here is my factory for appointment.rb:
require 'faker'
FactoryGirl.define do
factory :appointment do |f|
f.service {FactoryGirl.create(:service)}
f.appointment_time { Faker::Time.between(DateTime.now - 1, DateTime.now) }
end
end
Here is my appointment_spec.rb:
require 'spec_helper'
describe Appointment do
it "has a valid factory" do
FactoryGirl.create(:appointment).should be_valid
end
it "is invalid if it does not have a Service association" do
FactoryGirl.build(
:appointment, service: nil).should_not be_valid
end
end
I've been following the instructions listed here for making my Controller Spec. I've also found a lot of stackoverflow posts that say to do the same thing, yet I still get the same error.
Here are the tests not passing from my appointment_controller_spec.rb
describe AppointmentsController do
#other controller action code...
describe "POST #create" do
context "with valid attributes" do
it "saves the new appointment in the database" do
expect {
post :create, FactoryGirl.attributes_for(:appointment)
}.to change(Appointment, :count).by(1)
end
it "redirects to show page" do
post :create, FactoryGirl.attributes_for(:appointment)
response.should redirect_to Appointment.last
end
end
end
I'm at a loss and hoping some one can offer some insight.
EDIT:
As some of you had recommended, I changed the controller spec. This is actually what I had before I changed my code to what you see above:
it "saves the new appointment in the database" do
expect {
post :create, appointment: FactoryGirl.attributes_for(:appointment)
}.to change(Appointment, :count).by(1)
end
The reason I changed this is because when I had this my original error message was:
Failure/Error: expect {
count should have been changed by 1, but was changed by 0
Sorry for the confusion.
I believe you just need your AppointmentsController spec to read as follows:
describe AppointmentsController do
#other controller action code...
describe "POST #create" do
context "with valid attributes" do
it "saves the new appointment in the database" do
expect {
post :create, appointment: FactoryGirl.attributes_for(:appointment)
}.to change(Appointment, :count).by(1)
end
it "redirects to show page" do
post :create, appointment: FactoryGirl.attributes_for(:appointment)
response.should redirect_to Appointment.last
end
end
end
Adding appointment: before you supply the attributes via FactoryGirl in the post call.
Are you using strong_params in your controller? It looks like you are looking for an appointment param, but you are just getting a hash of the attributes.
Try this:
it "saves the new appointment in the database" do
expect {
post :create, appointment: FactoryGirl.attributes_for(:appointment)
}.to change(Appointment, :count).by(1)
end

Rspec with Guard and Spork will not run some tests, and doesn't give verbose error statements for failures

As I added tests to my spec, I became suspicious about every single one passing just as I wrote it, so I added some syntax errors into the code. It turned out, Rspec was not running my recent tests so the syntax errors weren't getting picked up. The comment in the code below shows the arbitrary line at which Rspec stopped showing a green period or red F for a test:
require 'spec_helper'
describe Api::PagesController do
def valid_session
{}
end
describe "GET index" do
before :each do
#page = create(:page)
end
it "assigns all pages as #pages" do
get :index
assigns(:pages).should eq([#page])
end
it "returns json" do
get :index, format: :json
expect(response.body).to have_content #page.to_json
end
end
describe "GET show" do
before :each do
#page = create(:page)
end
it "assigns the requested page as #page" do
get :show, {:id => #page.to_param}, valid_session
assigns(:page).should eq(#page)
end
it "returns json" do
get :show, {:id => #page.to_param}, valid_session, format: :json
expect(response.body).to have_content #page.to_json
end
end
describe "GET new" do
it "assigns a new page as #page" do
get :new, {}, valid_session
assigns(:page).should be_a_new(Page)
end
end
describe "GET edit" do
before :each do
#page = create(:page)
end
it "assigns the requested page as #page" do
get :edit, {:id => #page.to_param}, valid_session
assigns(:page).should eq(#page)
end
it "returns json" do
get :edit, {:id => #page.to_param}, valid_session, format: :json
expect(response.body).to have_content #page.to_json
end
end
describe "POST create" do
describe "with valid params" do
it "creates a new Page in the database" do
expect {
post :create, {page: attributes_for(:page)}, valid_session
}.to change(Page, :count).by(1)
end
it "assigns a newly created page as #page" do
post :create, {page: attributes_for(:page)}, valid_session
assigns(:page).should be_a(Page)
assigns(:page).should be_persisted
end
#################### TESTS BELOW HERE ARE NOT SHOWN BY RSPEC ##########
it "returns json" do
expect{
post :create, {page: attributes_for(:page)}, valid_session
}.to have_content page.to_json
end
end
describe "with invalid params" do
it "does not save the new page in the database" do
expect {
post :create, {page: attributes_for(:page_invalid)}, valid_session
}.to_not change(Page, :count).by(1)
end
#FUTURE TEST - redirects to new page with errors
end
end
describe "PUT update" do
before :each do
#page = create(:page)
end
describe "with valid params" do
it "updates the requested page" do
Page.any_instance.should_receive(:update_attributes).with({ "title" => "MyString" })
put :update, {:id => #page.to_param, :page => { "title" => "MyString" }}, valid_session
end
it "assigns the requested page as #page" do
put :update, {:id => #page.to_param, :page => { "title" => "MyString" }}, valid_session
assigns(:page).should eq(#page)
end
it "returns json" do
put :update, {:id => #page.to_param, :page => { "title" => "MyString" }}, valid_session, format: :jason
expect(response.body).to have_content #page.to_json
end
end
describe "with invalid params" do
it "assigns the page as #page" do
# Trigger the behavior that occurs when invalid params are submitted
Page.any_instance.stub(:save).and_return(false)
put :update, {:id => #page.to_param, page: attributes_for(:page_invalid)}, valid_session
assigns(:page).should eq(#page)
end
#FUTURE TEST - redirects to edit
end
end
describe "DELETE destroy" do
before :each do
#page = create(:page)
end
it "destroys the requested page" do
expect {
delete :destroy, {:id => #page.to_param}, valid_session
}.to change(Page, :count).by(-1)
end
end
describe "GET published" do
before :each do
#page = create(:page)
#page_unpublished = create(:page_unpublished)
end
it "returns a list of published pages only" do
get :published, format: :json
assigns(:pages).should eq([#page])
expect(response.body).to have_content #page_unpublished.to_json
end
end
describe "GET unpublished" do
before :each do
#page = create(:page)
#page_unpublished = create(:page_unpublished)
end
it "returns a list of unpublished pages only" do
get :unpublished, format: :json
assigns(:pages).should eq([#page_unpublished])
expect(response.body).to have_content #page_unpublished.to_json
end
end
describe "GET total_words" do
before :each do
#page = create(:page)
end
it "returns a list of unpublished pages only" do
get :total_words, {:id => #page.to_param}, format: :json
assigns(:total_words).should eq([#page_unpublished])
expect(response.body).to have_content #page_unpublished.to_json
end
end
end
When I intentionally added syntax errors to earlier tests, I saw red Fs instead of the green dots, but no error was being reported. Earlier, when there was an error, I'd see (for example):
16:10:14 - INFO - Running: spec/models/page_spec.rb
Running tests with args ["--drb", "-f", "progress", "-r", "/Users/user/.rvm/gems/ruby-1.9.3-p194/gems/guard-rspec-1.2.1/lib/guard/rspec/formatters/notification_rspec.rb", "-f", "Guard::RSpec::Formatter::NotificationRSpec", "--out", "/dev/null", "--failure-exit-code", "2", "spec/models/page_spec.rb"]...
.......F
Failures:
1) Page Publishing returns unpublished pages
Failure/Error: expect(Page.unpublished).to eq [page_unpublished]
NameError:
undefined local variable or method `page_unpublished' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_4:0x0000010355b8c0>
# ./spec/models/page_spec.rb:58:in `block (3 levels) in <top (required)>'
Finished in 0.54212 seconds
8 examples, 1 failure
Failed examples:
rspec ./spec/models/page_spec.rb:57 # Page Publishing returns unpublished pages
Done.
Now, when a test fails, I only see:
17:55:26 - INFO - Running: spec/controllers/pages_controller_spec.rb
Running tests with args ["--drb", "-f", "progress", "-r", "/Users/user/.rvm/gems/ruby-1.9.3-p194/gems/guard-rspec-1.2.1/lib/guard/rspec/formatters/notification_rspec.rb", "-f", "Guard::RSpec::Formatter::NotificationRSpec", "--out", "/dev/null", "--failure-exit-code", "2", "spec/controllers/pages_controller_spec.rb"]...
.....FF..Done.
This change happened at a pretty arbitrary time while coding, I didn't modify the Guardfile or any Spork configuration details. Any idea why this is happening?
It looks like one of the arguments to rspec is --out /dev/null. So your output is being redirected from STDOUT.
One possibility is that this is being caused by one or more bad tests. Have you tried commenting out various tests to see if you can isolate the problematic ones? If you're running your tests in order, I'd start with the ones just before and after the point at which rspec exits prematurely.

What is the proper way to test 'create' controller actions?

I am using Ruby on Rails 3.2.2, Rspec 2.9.0 and RspecRails 2.9.0. I would like to test the create controller action but I don't know how to make that the "right"/"proper" way. I "scaffolded" model, controller, view, ... files, so in those files I have the common code generated by Ruby on Rails generators; in my spec file I have:
it "assigns #article" do
new_article = FactoryGirl.build(:article)
Article.should_receive(:new).and_return(new_article)
post :create
assigns[:article].should eq(new_article)
end
Maybe, (note: the above code is almost the same as that I use to test the new controller action) a better way to test create controller actions would be to pass some attribute value during the post :create action instead of proceed as I make above, but I don't know how to make that and if it is the "right"/"proper" way to make things.
So, what is the proper way to test 'create' controller actions?
I'm doing it this way:
describe "#create" do
before { post :create, { "my_model"=> { "name"=>"name" } } }
specify("should created one my_model") { change{ MyModel.count }.from(0).to(1) }
end
Aaron Sumner who recently wrote the book Everyday Rails Testing with RSpec have an article at his blog. Where he describes it like this:
describe "POST create" do
context "with valid attributes" do
it "creates a new contact" do
expect{
post :create, contact: Factory.attributes_for(:contact)
}.to change(Contact,:count).by(1)
end
it "redirects to the new contact" do
post :create, contact: Factory.attributes_for(:contact)
response.should redirect_to Contact.last
end
end
context "with invalid attributes" do
it "does not save the new contact" do
expect{
post :create, contact: Factory.attributes_for(:invalid_contact)
}.to_not change(Contact,:count)
end
it "re-renders the new method" do
post :create, contact: Factory.attributes_for(:invalid_contact)
response.should render_template :new
end
end
end
How about:
it "creates article" do
article_params = FactoryGirl.attributes_for(:article)
expect { post :create, :article => article_params }.to change(Article, :count).by(1)
end

Resources