Create method test keeps failing with RSpec - ruby-on-rails

Alright, I keep trying to make this test work and for some reason it does not want to work. I don't know if I missed something or what but I can't figure out what the heck is going on with this code. Every thing seems to point to it being correct but I don't know. Anyway here is what I have done, which I think is correct but obviously it isn't since it keeps failing.
These are my test attributes, the invalid ones are failing for some reason.
let(:valid_attributes){
#user = User.create!(:email => "email#gmail.com", :password => 'password')
{:name => "name", :user_id => #user.id}
}
let(:invalid_attributes){
#user = User.create!(:email => "email#gmail.com", :password => 'password')
{:name => "", :user_id => #user.id}
}
Here's my post request:
describe "POST #create" do
context "with valid attributes" do
it "describes a survey created with valid attributes" do
expect{
post :create, survey: valid_attributes
}.to change(Survey, :count).by(1)
end
it "redirects the user to the survey's detail page" do
post :create, {survey: valid_attributes}
expect(response).to redirect_to(Survey.last)
end
end
context "with invalid attributes" do
it "describes a survey created with invalid attributes" do
post :create, {survey: invalid_attributes}
expect(assigns(:survey)).to be_a_new(Survey)
end
it "re-renders the new template" do
post :create, {survey: invalid_attributes}
expect(response).to render_template('new')
end
end
end
And of course my controller method, which is implemented, as such this shouldn't be failing, especially because it is doing exactly what that stuff indicates.
def create
#survey = Survey.new(survey_params)
respond_to do |format|
if #survey.save
format.html { redirect_to #survey, notice: 'Survey was successfully created.' }
else
format.html { render :new }
end
end
end
I'm using strong parameters as well, don't know if that makes a difference, I don't think it should but anyway this is what they have:
def set_survey
#survey = Survey.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def survey_params
params.require(:survey).permit(:name, :user_id)
end
Finally, this is what my error messages say, which make no sense to me, especially the first one since the object seems to meet all the standards for it to be a survey.
Error 1:
1) SurveysController POST #create with invalid attributes describes a survey created with invalid attributes
Failure/Error: expect(assigns(:survey)).to be_a_new(Survey)
expected #<Survey id: 187, name: "", user_id: 257, created_at: "2015-10-11 04:46:35", updated_at: "2015-10-11 04:46:35"> to be a new Survey(id: integer, name: string, user_id: integer, created_at: datetime, updated_at: datetime)
# ./spec/controllers/surveys_controller_spec.rb:82:in `block (4 levels) in <top (required)>'
Error 2:
2) SurveysController POST #create with invalid attributes re-renders the new template
Failure/Error: expect(response).to render_template('new')
expecting <"new"> but rendering with <[]>
# ./spec/controllers/surveys_controller_spec.rb:87:in `block (4 levels) in <top (required)>'

What is happening is when you send a request using invalid_attributes, your controller still takes the successful path through your code. You can tell from your two failures. Rendering with <[]> happens on a redirect, and object instances only have an id and created_at if they've been saved.
This would indicate that you're not validating presence of name in your Survey model:
# app/models/survey.rb
class Survey < ActiveRecord::Base
belongs_to :user
validates :name, presence: true
end
and thus it is saved successfully with a blank name.

Related

RSpec - unable to get all associate attributes in controller

Hello Programmers & Developers!!!, I'm a beginner in RoR and creating a simple project in rails to learn its working, so in that project I'm facing a problem in writing a spec for the create method of controller. When I'm trying to pass the associate attributes of the object in spec file, in controller it isn't get all the attributes.
In the create method of subjects_controller.rb file.I've created a variable called attr in this variable I'm storing all the values sent from subjects_controller_spec.rb file.
attr=(params.require(:subject).permit(:name)).merge(:classroom_ids=>params[:subject][:classroom_ids],:school_ids=>params[:subject][:school_ids])
Now, If I print the value of the attr using p attr in console it's output is the exact output that I want, which is
{"name"=>"Computer", "classroom_ids"=>["1", "2"], "school_ids"=>["1"]}
But, now I'm doing #subject = Subject.new(attr) and printing value of #subject gives the following output
#<Subject id: nil, name: "Computer", created_at: nil, updated_at: nil>
and after running the test I'm getting my test failed and then I printed the error p #subject.errors it gave me the below output
#<ActiveModel::Errors:0x007fc35444a218 #base=#<Subject id: nil, name: "Computer", created_at: nil, updated_at: nil>, #messages={:school_ids=>["is not a number"], :classroom_ids=>["is not a number"]}>
So, here is my actual question is why #subject in subjects_controller.rb is not having values of classroom_ids and school_ids? If any solution or suggestion is there then please help me to sort out this problem.
Below I'm providing you all the necessary details to understand the actual problem.
Ruby Version 2.2.4
Rails Version 4.2.0
Database MySQL
Model file subject.rb
class Subject < ActiveRecord::Base
has_and_belongs_to_many :schools
has_and_belongs_to_many :teachers
has_and_belongs_to_many :classrooms
has_and_belongs_to_many :students
validates_presence_of :name, :school_ids, :classroom_ids
validates_numericality_of :school_ids, :classroom_ids
end
Controller file subjects_controller_spec.rb
require 'rails_helper'
RSpec.describe SubjectsController, type: :controller do
before(:each) do
#school1 = FactoryGirl.create(:school)
#classroom1 = FactoryGirl.create(:classroom, :school_id=>#school1.id)
#classroom2 = FactoryGirl.create(:classroom, :school_id=>#school1.id)
#subject = FactoryGirl.build(:subject)
#subject.classrooms<<#classroom1
#subject.classrooms<<#classroom2
#subject.schools<<#school1
end
context "POST create" do
it "should be success" do
# p #subject
# p #subject.classrooms
# p #subject.classroom_ids
attributes=#subject.attributes.merge(:classroom_ids=>#subject.classroom_ids,:school_ids=>#subject.school_ids)
# In below line, I'm sending all the values to the controller to create a new subject.
post :create, :subject=>attributes
response.status.should eq 201
end
end
end
Controller file subjects_controller.rb
class SubjectsController < ApplicationController
before_action :set_subject, only: [:show, :edit, :update, :destroy]
# GET /subjects
def index
#subjects = Subject.all
end
# GET /subjects/1
def show
end
# GET /subjects/new
def new
#subject = Subject.new
end
# GET /subjects/1/edit
def edit
end
# POST /subjects
def create
attr=(params.require(:subject).permit(:name)).merge(:classroom_ids=>params[:subject][:classroom_ids],:school_ids=>params[:subject][:school_ids])
p attr ### here it prints all the values which I want to create subject.###
#subject = Subject.new(attr)
p #subject ### here is the actual problem, It's not printing all the values that need to create a new subject.###
if #subject.save
redirect_to #subject, notice: 'Subject was successfully created.', status: :created
else
p #subject.errors
render :new, status: :unprocessable_entity
end
end
# PATCH/PUT /subjects/1
def update
if #subject.update(subject_params)
redirect_to #subject, notice: 'Subject was successfully updated.', status: :ok
else
render :edit, :status => :unprocessable_entity
end
end
# DELETE /subjects/1
def destroy
#subject.destroy
redirect_to subjects_url, notice: 'Subject was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_subject
#subject = Subject.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def subject_params
params.require(:subject).permit(:name, :school_ids, :classroom_ids)
end
end
Factory file subjects.rb
FactoryGirl.define do
factory :subject do
name "Computer"
end
end
RSpec Test Report
rspec spec/controllers/subjects_controller_spec.rb
{"name"=>"Computer", "classroom_ids"=>["1", "2"], "school_ids"=>["1"]}
#<Subject id: nil, name: "Computer", created_at: nil, updated_at: nil>
#<ActiveModel::Errors:0x007fcdfe8f1a28 #base=#<Subject id: nil, name: "Computer", created_at: nil, updated_at: nil>, #messages={:school_ids=>["is not a number"], :classroom_ids=>["is not a number"]}>
F
Failures:
1) SubjectsController POST create should be success
Failure/Error: response.status.should eq 201
expected: 201
got: 422
(compared using ==)
# ./spec/controllers/subjects_controller_spec.rb:21:in `block (3 levels) in <top (required)>'
Deprecation Warnings:
Using `should` from rspec-expectations' old `:should` syntax without explicitly enabling the syntax is deprecated. Use the new `:expect` syntax or explicitly enable `:should` with `config.expect_with(:rspec) { |c| c.syntax = :should }` instead. Called from /Users/vishal/project/school_system/spec/controllers/subjects_controller_spec.rb:21:in `block (3 levels) in <top (required)>'.
If you need more of the backtrace for any of these deprecations to
identify where to make the necessary changes, you can configure
`config.raise_errors_for_deprecations!`, and it will turn the
deprecation warnings into errors, giving you the full backtrace.
1 deprecation warning total
Finished in 0.40113 seconds (files took 3.03 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/controllers/subjects_controller_spec.rb:14 # SubjectsController POST create should be success
Coverage report generated for RSpec to /Users/vishal/project/school_system/coverage. 49 / 332 LOC (14.76%) covered.
For more details you can refer this Github link.
Thanks For Help In Advance.
[ "1", "2" ] is not array of integer but String! #subject has classroom_ids and school_ids, but params always treat input values as String, so validation error occurs in your Subject model. So try below to transform String to Integer:
params[:subject][:classroom_ids].map(&:to_i)
params[:subject][:school_ids].map(&:to_i)
How about this?
restore below without map method in the controller:
params[:subject][:classroom_ids]
params[:subject][:school_ids]
In my PC, by modifing the Subject model as below from your github link and passed the test.
Could you try this?
Class Subject < ActiveRecord::Base
has_and_belongs_to_many :schools
has_and_belongs_to_many :teachers
has_and_belongs_to_many :classrooms
has_and_belongs_to_many :students
validates_presence_of :name, :school_ids, :classroom_ids
validate :validate_classroom_ids
validate :validate_school_ids
private
def validate_classroom_ids
if classroom_ids.any?{ |id| !id.is_a?(Integer) }
errors.add(:classroom_ids, 'is not a number')
return false
end
end
def validate_school_ids
if school_ids.any?{ |id| !id.is_a?(Integer) }
errors.add(:school_ids, 'is not a number')
return false
end
end
end

Rails 4: Factory Girl & Rspec with associated Model

I previously fixed an issue with some code that works though it is a little ugly. Problem now is that it breaks my tests! The idea here is that I can create a Campaign and associate 1 zip-file and one-to-many pdfs.
Previous question and solution:
Rails 4.2: Unknown Attribute or Server Error in Log
Here is the failure message:
console
1) CampaignsController POST #create with valid params
Failure/Error: post :create, campaign: attributes_for(:campaign)
ActiveRecord::RecordNotFound:
Couldn't find Uploadzip with 'id'=
# ./app/controllers/campaigns_controller.rb:15:in `create'
# ./spec/controllers/campaigns_controller_spec.rb:36:in `block (4 levels) in <top (required)>'
..and the rest of the code.
spec/factories/campaigns.rb
FactoryGirl.define do
factory :campaign do |x|
x.sequence(:name) { |y| "Rockfest 201#{y} Orange County" }
x.sequence(:comment) { |y| "Total attendance is #{y}" }
end
end
spec/controllers/campaigns_controller.rb
describe "POST #create" do
context "with valid params" do
before(:each) do
post :create, campaign: attributes_for(:campaign)
end
.........
end
app/controllers/campaigns_controller.rb
class CampaignsController < ApplicationController
......................
def create
#campaign = Campaign.new(campaign_params)
if #campaign.save
zip = Uploadzip.find(params[:uploadzip_id])
zip.campaign = #campaign
zip.save
flash[:success] = "Campaign Successfully Launched!"
redirect_to #campaign
else
................
end
end
.......................
private
def campaign_params
params.require(:campaign).permit(:name, :comment, :campaign_id, uploadpdf_ids: [])
end
end
This appears simple and I assume it is, yet I've tried quit a few things and can't seem to get it to pass. How would I support the new controller logic in this test? Any help is appreciated.
UPDATE
With zetitic's advice, I created the following code in which successfully passes.
before(:each) do
#uploadzip = create(:uploadzip)
post :create, campaign: attributes_for(:campaign), uploadzip_id: #uploadzip
end
Add the uploadedzip_id to the posted params:
before(:each) do
post :create, campaign: attributes_for(:campaign), uploadedzip_id: 123456
end

rspec controller testing, Expected response to be a <redirect>, but was <200>

I'm using rspec and Factory Girl for testing. When testing the POST #create section of my posts_controller I'm getting the error in the title.
Failures:
1) PostsController POST #create with valid attributes redirects to the post
Failure/Error: response.should redirect_to Post.last
Expected response to be a <redirect>, but was <200>
# ./spec/controllers/posts_controller_spec.rb:59:in `block (4 levels) in <top (required)>'
This is the code from the spec being tested. I'm sure it's not the most efficient way of doing this, but it does work.
def create
#post = Post.new(
:text => post_params[:text],
:embed => post_params[:embed],
:user => current_user,
:title => post_params[:title],
:tag_list => post_params[:tag_list],
:cagegory_ids => post_params[:category_ids]
)
if #post.save
redirect_to #post
else
render 'new'
end
end
...
private
def post_params
params.require(:post).permit(:title, :text, :embed, :user_id, :tag_list,
:category_ids => [])
end
Here's the factory.
FactoryGirl.define do
factory :post do
title { Faker::Lorem.characters(char_count = 20) }
text { Faker::Lorem.characters(char_count = 150) }
user
categories {
Array(5..10).sample.times.map do
FactoryGirl.create(:category)
end
}
end
end
And the relevant part of the spec
describe "POST #create" do
context "with valid attributes" do
it "saves the new post" do
expect{
post :create, post: FactoryGirl.create(:post).attributes
}.to change(Post,:count).by(1)
end
it "redirects to the post" do
post :create, post: FactoryGirl.create(:post).attributes
response.should redirect_to Post.last
end
end
end
The other test, "saves the new post," works fine. I've tried other variations of the redirect_to line, such as "redirect_to(posts_path(assigns[:post])) and it throws the same error.
Any ideas?
OK, I fixed my problem. It isn't pretty but it works.
The issue is with Factory Girl and associations, and I'm definitely not the first to have that problem, but none of the other solutions worked.
I ended up adding this before :each at the top of the POST #create section...
describe "POST #create" do
before :each do
Post.destroy_all
#cat = FactoryGirl.create(:category)
#newpost = FactoryGirl.build(:post)
#post_params = {category_ids: [#cat.id]}.merge(#newpost.attributes)
end
...
...to put the post params into a new hash that I could call later on in the code like this...
it "saves the new post" do
expect{
post :create, post: #post_params
}.to change(Post,:count).by(1)
end
it "redirects to the post" do
post :create, post: #post_params
response.should redirect_to Post.last
end
So this is solved. It adds a bit of overhead to the test but it works. I won't mark this as THE solution for a couple of days in case someone else comes around with some better, simpler code. I definitely welcome any more ideas.
I assume the problem is in factory. Most likely post instance didn't pass validations and controller renders new view, which is not redirect, but success 200. Add logger in the controller and take a look if record actually saves. You can also read through test log tail -f log/test.log.

post create test for user count failing. Should be changed by 1, but was changed by 0

Been trying to figure this out for a while now, but can't seem to go green. Here is the rspec error that I am getting:
1) UsersController POST create with valid parameters creates a new user
Failure/Error: expect {post :create, user}.to change(User, :count).by 1
count should have been changed by 1, but was changed by 0
# ./spec/controllers/users_controllers_spec.rb:36:in `block (4 levels) in <top
(required)>'
Here is my relevant test in the users_controllers_spec:
describe UsersController do
let(:user) {FactoryGirl.create :user}
context 'POST create' do
context 'with valid parameters' do
before {post :create, user}
let(:user) {FactoryGirl.build :user}
it 'creates a new user' do
expect {post :create, user}.to change(User, :count).by 1
end
it {should respond_with 200}
end
And here is my users_controller create method:
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome #{#user.name.capitalize}! Please complete your profile."
redirect_to profile_path(current_user)
else
render :new
end
end
Also, here is my factories.rb file:
FactoryGirl.define do
factory :user do
name "John"
email "John#example.com"
password "password"
password_confirmation "password"
end
end
What am I missing? I feel like it is something really obvious, but I can't seem to figure it out. Any help would be much appreciated.
Your test involves creating a second user with FactoryGirl, but the creation of the second user is failing because the email is the same as the first and you have a uniqueness validation on that attribute.
You can either not create the first user at all or you'll need to make the emails different (e.g. by passing in an email parameter to your FactoryGirl call or changing the Factory to make each email address unique).

How to post to create with spec helper method in controller spec?

I'm relatively new to programming, Rails, Ruby, Rspec, and the like, so thanks for your help!
My specs were very repetitive, so I wrote some spec helper methods. I can't figure out how to properly use them in my specs. Specifically, I have a users controller with create:
def create
#user = User.new(params[:user])
if #user.save
redirect_to user_path(#user)
else
render :action => :new
end
end
A bit in the spec helper that creates a valid user:
def valid_user_eilif
#test_image = Rails.root + "spec/fixtures/images/seagull.jpg"
#file = Rack::Test::UploadedFile.new(#test_image, "image/jpeg")
user = User.create!(:username => "eilif", :email => "eilif#email.org",
:image => #file, :bio => "Lots of text that I don't want to write",
:signature_quote => "Yet more text.")
user.save!
user
end
And then in my user controller spec:
before (:each) do
post :create, :user => valid_user_eilif
end
it 'should assign user to #user' do
assigns(:user).should eq(User.last)
end
When I run the spec I get the error:
Failure/Error: assigns(:user).should eq(User.last)
expected #<User id: 1, username: "eilif", email: "eilif#email.org", bio: "Lots of text that I don't want to write", signature_quote: "I feel empty.", image_file_name: "seagull.jpg", image_content_type: "image/jpeg", image_file_size: 10475, image_updated_at: "2011-05-10 23:35:55", created_at: "2011-05-10 23:35:56", updated_at: "2011-05-10 23:35:56">
got #<User id: nil, username: nil, email: nil, bio: nil, signature_quote: nil, image_file_name: nil, image_content_type: nil, image_file_size: nil, image_updated_at: nil, created_at: nil, updated_at: nil>
So, I assume I'm incorrectly posting to create, since nothing is created? What's the proper way to do this?
Ideally controller specs shouldn't depend on the model being able to create a row in the database. With such a simple action you can mock out the dependencies:
describe UsersController do
context "on success" do
before(:each) do
#user = mock_model(User,:save=>true)
User.stub(:new) {#user}
post :create, :user => {}
end
it "redirects" do
response.should redirect_to(user_path(#user))
end
it "assigns" do
assigns[:user].should == #user
end
end
context "on failure" do
it "renders 'new'" do
#user = mock_model(User,:save=>false)
User.stub(:new) {#user}
post :create, :user => {}
response.should render_template "users/new"
end
end
end
Notice that the specs don't pass anything in params[:user]. This helps enforce the MVC separation of concerns, whereby the model is responsible for handling the attributes, ie. validating, setting up associations, etc. You can't always keep controllers this 'skinny', but it's a good idea to try.
It looks like the problem is that #user doesn't get refreshed after the save. Try assigns(:user).reload.should eql(User.last).
But there's another slight problem, and that's probably still going to fail. You shouldn't be calling post with :user => valid_user_eilif; you want the attributes from your user record, not the actual user object itself. And you're essentially creating a new user in valid_user_eilif and then making your controller create that object again -- if you have any kind of unique constraints, you're going to get a conflict.
This is a good place to use something like factory_girl and mocks. For an example, take a look at how one of my projects handles controller specs. This example uses factory_girl, Mocha and shoulda. I'll annotate it with comments below:
describe MembersController, "POST create" do
before do
# Factory Girl - builds a record but doesn't save it
#resource = Factory.build(:member)
# Mocha expectation - overrides the default "new" behavior and makes it
# return our resource from above
Member.expects(:new).with({}).returns(#resource)
# Note how we expect it to be called with an empty hash; that's from the
# `:member` parameter to `post` below.
end
context "success" do
before do
post :create, :member => {}
end
# shoulda matchers - check for a flash message and a redirect
it { should set_the_flash.to(/successfully created/) }
it { should redirect_to(member_path(#resource)) }
end
context "failure" do
before do
# Mocha - To test a failing example in the controller, we override the
# default `save` behavior and make it return false, otherwise it would
# be true
#resource.expects(:save).returns(false)
post :create, :member => {}
end
# shoulda matchers - check for no flash message and re-render the form
it { should_not set_the_flash }
it { should render_template(:new) }
end
end

Resources