Rspec having difficulty testing #create with build controller method - ruby-on-rails

So if your #create controller method is simply:
#testimonial = Testimonial.new(testimonial_params)
And you test it in your specs like so:
testimonials_controller_spec.rb
describe "POST #create" do
context "with VALID attributes" do
it "creates new testimonial" do
expect {
post :create, testimonial: FactoryGirl.attributes_for(:testimonial)
}.to change(Testimonial, :count).by(1)
end
end
end
It works fine. The code:
post :create, testimonial: FactoryGirl.attributes_for(:testimonial)
is correct.
However, in my TestimonialsController, my create method is actually:
#testimonial = current_user.testimonials.build(testimonial_params)
My rspec method doesn't work with this. What should I use instead of:
post :create, testimonial: FactoryGirl.attributes_for(:testimonial)
?

Sign in the user before calling the controller action. Please find the following:
#testimonials_controller_spec.rb
require 'rails_helper'
describe TestimonialsController, type: :controller do
let(:user) do
FactoryGirl.create :user
end
before do
sign_in user
end
describe "POST #create" do
context "with VALID attributes" do
it "creates new testimonial" do
expect {
post :create, testimonial: FactoryGirl.attributes_for(:testimonial)
}.to change(Testimonial, :count).by(1)
end
end
end
end

Build doesn't save/persist the record to the database. Why not make it:
#testimonial = current_user.testimonials.new(testimonial_params)
#testimonial.save

Related

How to access instance variables to test `receive` in a spec?

I have the following spec fragment:
it 'should create company and user' do
company_iv = assigns(:company)
user_iv = assigns(:user)
expect(subject).to receive(:create_timeline_event).with(company_iv, user_iv)
expect { post :create, params }.to change { User.count }.by(1).and change { Company.count }.by(1)
and traditionally use the receive syntax to test calling a method. I normally call it before the call to post in the above fragment. How would I access the instance variable of the user and the company for this spec?
Looks like you're trying to jam a few different tests into a single it statement. Here's how I would approach this:
it 'creates company and user' do
expect { post :create, params }
.to change { User.count }.by(1)
.and change { Company.count }.by(1)
end
it 'assigns instance variables' do
post :create, params
expect(assigns(:company)).to eq(Company.last)
expect(assigns(:user)).to eq(User.last)
end
it 'calls create_timeline_event with newly created company and user' do
allow(some_object).to receive(:create_timeline_event)
post :create, params
expect(some_object)
.to have_received(:create_timeline_event)
.with(Company.last, User.last)
end
Note that these tests are going to be slow because they hit the database. Another approach to this is to use mocks. That would look something like this:
let(:params) { ... }
let(:company) { instance_double(Company) }
let(:user) { instance_double(User) }
before do
allow(Company).to receive(:create).and_return(company)
allow(User).to receive(:create).and_return(user)
allow(some_object).to receive(:create_timeline_event)
post :create, params
end
it 'creates company and user' do
expect(Company).to have_received(:create).with(company_params)
expect(User).to have_received(:create).with(user_params)
end
it 'assigns instance variables' do
expect(assigns(:company)).to eq(company)
expect(assigns(:user)).to eq(user)
end
it 'calls create_timeline_event with newly created company and user' do
expect(some_object)
.to have_received(:create_timeline_event)
.with(company, user)
end
These tests do not hit the database at all, meaning that they'll execute much faster.

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

Rails Controller testing

I am doing the thoughtbot intro to testing program. Im not sure how to test for what they want.
Below is my test.
require "rails_helper"
describe PeopleController do
describe "#create" do
context "when person is valid" do
it "redirects to #show" do
post :create, FactoryGirl.build_stubbed(:person)
expect(response).to redirect_to(show_people_path)
end
end
context "when person is invalid" do
it "redirects to #new" do
pending "create this test"
end
end
end
end
I am of course using factory girl. I have tried several methods. I really don't know hoe to test this controller.
Any insights would be great.
I would create an 'invalid' person using the FactoryGirl, and send it as a parameter to the post :create.
To create an invalid person record, why don't you use nested factories in FactoryGirl? Depending on the validation in your model, you can simply do something like:
spec/factories/person.rb
FactoryGirl.define do
factory :person do
...
factory :invalid_person do
...
email nil
...
end
end
end
in your test
context "when person is invalid" do
it "redirects to #new" do
post :create, FactoryGirl.build_stubbed(:invalid_person)
expect(response).to redirect_to action: :new
end
end

Errors in RSpec test when using attributes_for with my factory

I've got the following factory:
FactoryGirl.define do
factory :category do
name "Example category"
user_id 1
end
end
This is my test:
describe "POST #create" do
context "signed in" do
before(:each) do
sign_in :user, user
end
context "with valid attributes" do
it "saves the new category in the database" do
expect{
post :create, FactoryGirl.attributes_for(:category)
}.to change(Category, :count).by(1)
end
it "redirects to category #index"
end
context "with invalid attributes" do
it "does not save the new category in the database"
it "re-renders the :new template"
it "provides errors"
end
end
context "not signed in" do
it "blocks access (somehow)"
end
end
But, I'm getting the following error when I run my test:
3) CategoriesController POST #create signed in with valid attributes saves the new category in the database
Failure/Error: post :create, FactoryGirl.attributes_for(:category)
ActionController::ParameterMissing:
param not found: category
I've managed to use the factory previously, it works fine in my spec/model/ tests. Is there anything I'm doing wrong here? I can't see any problem with the factory. I'm a little lost :(
You have to provide the key:
post :create, category: FactoryGirl.attributes_for(:category)

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