Rspec not changing count on create - ruby-on-rails

I am trying to resolve an issue with my rspec test to create an object but the count doesn't seem to change whatever i try. I am sure i am missing something very basic here.
Here is my rspec:
before do
login_account_admin(user)
#group = Factory(:group, :code => "GR_111", :description => "description for GR_111")
Group.stub!(:find).and_return(#group)
end
describe "#create" do
it "should create a new group object" do
group_params = {:code => "NEW_GROUP", :description => "description for NEW_GROUP"}
expect {
post :create, :service_id => service, :cdb_group => group_params, :button => "save", :format => "js"
}.to change(Group, :count).by(1)
end
it "should not create a new group object with invalid code format" do
group_params = {:code => "invalid", :description => "description for invalid code name group"}
expect {
post :create, :service_id => service, :cdb_group => group_params, :button => "save", :format => "js"
}.to_not change(Group, :count)
end
end
"code" parameter can only contain uppercase letters A to Z, 0-9 and _
Here is the controller method definition for #create
def create
#group = Group.new(params[:cdb_group])
respond_to do |format|
if params[:button] == "cancel"
format.js { render "hide_new"}
elsif #group.save
format.js {
render 'show_new_group'
}
format.html { redirect_to(some_path(#service), :notice => 'Group was successfully created.') }
format.xml { head :ok }
end
end
end
Here is the Group model:
class Group < ActiveRecord::Base
validates_uniqueness_of :code
validates_presence_of :code, :description
validates_format_of :code, :without => /[^A-Z0-9_]/ , :message => 'can only contain uppercase letters A to Z, 0-9 and _'
end
Whenever i try to run the rspec test I get the following errors:-
1) GroupsController User As Account Admin goes to #create should create a new group object
Failure/Error: expect {
count should have been changed by 1, but was changed by 0
# ./spec/controllers/groups_controller_spec.rb:51
2) GroupsController User As Account Admin goes to #create should not create a new group object with invalid code format
Failure/Error: expect {
count should not have changed, but did change from 2 to 1
# ./spec/controllers/groups_controller_spec.rb:58
Any help in this regard would be highly appreciated?

Whenever our tests give us unexpected trouble, it's important to take a step back and re-evaluate our approach. Usually, this is an indication of some design problem, either with the code we're testing or with tests themselves.
While it sounds like using a truncation strategy has fixed this particular problem (see more on that below), i would suggest that there is more to learn from the situation.
Consider the two examples from your spec above. The only difference between them comes down to whether the code parameter is valid or not. I would argue that these examples are really testing the Group model, not the controller.
Now, if we're confident in our model test coverage, then we can take a different approach to the controller spec. From the controller's perspective, the model is a collaborator and in general, we always want to avoid indirectly testing collaborators. In this case, we can use a mock to simulate the behavior of the Group model and only test the controller behavior in isolation.
Something like this (please note the code below is incomplete and untested):
# spec/controllers/groups_controller_spec.rb
describe "#create" do
before do
# use a Test Double instead of a real model
#new_group = double(Group)
#params = { :cdb_group => 'stub_cdb_group_param', :service_id => service }
# using should_receive ensures the controller calls new correctly
Group.should_receive(:new).with(#params[:cdb_group]).and_return(#new_group)
end
context "when cancelled responding to js" do
it "renders hide_new" do
post :create, #params.merge({:button => "cancel", :format => "js"})
expect(response).to render_template('hide_new')
end
end
context "with valid params" do
before do
#new_group.should_receive(:save).and_return(true)
end
context "responding to json" # ...
context "responding to html" # ...
context "responding to xml" #...
end
context "with invalid params" do
before do
#new_group.should_receive(:save).and_return(false)
end
# ...
end
end
While the above doesn't specifically address the problem with record counts you were having, i suspect the problem may go away once you isolate your test targets correctly.
If you choose to stick with database truncation, consider using it selectively as described here.
I hope at least some of that helps :).

After fiddling with my spec_helper.rb file. It turns out that i have to change my database cleaning strategy to truncation. Here is my spec_helper file, for reference (https://gist.github.com/aliibrahim/7152042)
I changed this line in my code and disable use of transactional_fixtures
config.use_transactional_fixtures = false
and my database cleaning strategy is now:
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean_with(:truncation)
end
This gives a clear database before the start/end of every scenario. Hope this helps anyone!

You should test...
1) Group.create(group_params).should be_true after group_params = ...
If this fails, the problem probably related to model or test environment.
2) response.status.should == 302 after post ...
If this fails, the problem probably related to session (authentication / authorization).
3) assigns(:group).should be_valid after post ...
If this fails, the problem probably related to controller.

Related

Trying to retrieve a record with get :index, based on user, nothing getting returned. Response OK

I'm trying to retrieve a record based on a user id. To make sure that my get controller's index works appropriately. Here is my controller snippet.
class SimulationsController < ApplicationController
def index
if current_user
#simulations = current_user.simulations
else
redirect_to new_user_session_path, notice: 'You are not logged in.'
end
end
Now I put some traces in my below controller spec. From what I'm gatherin from these traces there are four Simulations that exist for the test with the User_ID 1, however the User thats being created for the test, and the records along with it in the test are all User_ID 5. Can anyone give some guidance I'm pretty stumped beating my head on this. Additionally I'm getting a Response: OK. EDIT: Updated Spec with below answer.
require 'spec_helper'
describe SimulationsController, :type => :controller do
let :user do
FactoryGirl.create(:user)
end
before(:each) do
puts "~#{user.id}"
sign_in user
end
describe "GET #index" do
it "returns the correct number of simulations" do
simulation = FactoryGirl.build(:simulation, user: user)
simulation.save!
puts "#####{simulation.user_id}"
puts user.id
Simulation.all.each do |sim|
puts sim.user_id
end
get :index
puts "---\t\t#{response.body.size}"
# expect(response).to be_success
end
end
end
EDIT 2:
User Factory:
FactoryGirl.define do
factory :user do
email "user_#{User.last.nil? ? 1 : User.last.id + 1}#home.com"
password "password"
end
end
Simulation Factory:
FactoryGirl.define do
factory :simulation do |f|
f.id (Simulation.last.nil? ? 1 : Simulation.last.id + 1)
f.x_size 3
f.y_size 3
f.user_id 1
end
end
Final Edit: I was going about checking wrong, as outlined below, Body isn't what I was looking for I wanted to use assigns as I did below to check what I wanted too:
it "returns the correct number of simulations" do
simulation = FactoryGirl.build(:simulation, user: user)
simulation.save!
get :index
expect(assigns(:simulations).size).to eq 1
end
response.body is the rendered HTML, so its size would be length of that string. Depending on what your view looks like there might not be a straightforward correlation between its size and the number of simulations rendered.
In addition by default rspec doesn't render views at all in controller specs and so response.body will always be an empty string. You can change this by adding render_views to the example group.
Probably FactoryGirl overrides your user_id assignment, because you have association :user being set up there. Just change user_id to user and it should work:
simulation = FactoryGirl.build(:simulation, user: user)
UPD. And fix your factory:
FactoryGirl.define do
factory :simulation do |f|
# Never set ID manually
# f.id (Simulation.last.nil? ? 1 : Simulation.last.id + 1)
f.x_size 3
f.y_size 3
# f.user_id 1
# user 'association' method to set up associations
f.association :user
end
end
UPD2. To check if you controller assigned variable properly, use assigns:
expect(assigns(:simulations).length).to eq 4
You almost never should compare your response.body to anything – because, well, it's just raw body. To test your views you use special expectation methods, and to check instance #-variables assignments you use assigns.

Mocking and stubbing in testing

I've recently learned how to stub in rspec and found that some benefits of it are we can decouple the code (eg. controller and model), more efficient test execution (eg. stubbing database call).
However I figured that if we stub, the code can be tightly tied to a particular implementation which therefore sacrifice the way we refactor the code later.
Example:
UsersController
# /app/controllers/users_controller.rb
class UsersController < ApplicationController
def create
User.create(name: params[:name])
end
end
Controller spec
# /spec/controllers/users_controller_spec.rb
RSpec.describe UsersController, :type => :controller do
describe "POST 'create'" do
it 'saves new user' do
expect(User).to receive(:create)
post :create, :name => "abc"
end
end
end
By doing that didn't I just limit the implementation to only using User.create? So later if I change the code my test will fail even though the purpose of both code is the same which is to save the new user to database
# /app/controllers/users_controller.rb
class UsersController < ApplicationController
def create
#user = User.new
#user.name = params[:name]
#user.save!
end
end
Whereas if I test the controller without stubbing, I can create a real record and later check against the record in the database. As long as the controller is able to save the user Like so
RSpec.describe UsersController, :type => :controller do
describe "POST 'create'" do
it 'saves new user' do
post :create, :name => "abc"
user = User.first
expect(user.name).to eql("abc")
end
end
end
Really sorry if the codes don't look right or have errors, I didn't check the code but you get my point.
So my question is, can we mock/stub without having to be tied to a particular implementation? If so, would you please throw me an example in rspec
You should use mocking and stubbing to simulate services external to the code, which it uses, but you are not interested in them running in your test.
For example, say your code is using the twitter gem:
status = client.status(my_client)
In your test, you don't really want your code to go to twitter API and get your bogus client's status! Instead you stub that method:
expect(client).to receive(:status).with(my_client).and_return("this is my status!")
Now you can safely check your code, with deterministic, short running results!
This is one use case where stubs and mocks are useful, there are more. Of course, like any other tool, they may be abused, and cause pain later on.
Internally create calls save and new
def create(attributes = nil, options = {}, &block)
if attributes.is_a?(Array)
attributes.collect { |attr| create(attr, options, &block) }
else
object = new(attributes, options, &block)
object.save
object
end
end
So possibly your second test would cover both cases.
It is not straight forward to write tests which are implementation independent. That's why integration tests have a lot of value and are better suited than unit tests for testing the behavior of the application.
In the code you're presented, you're not exactly mocking or stubbing. Let's take a look at the first spec:
RSpec.describe UsersController, :type => :controller do
describe "POST 'create'" do
it 'saves new user' do
expect(User).to receive(:create)
post :create, :name => "abc"
end
end
end
Here, you're testing that User received the 'create' message. You're right that there's something wrong with this test because it's going to break if you change the implementation of the controllers 'create' action, which defeats the purpose of testing. Tests should be flexible to change and not a hinderance.
What you want to do is not test implementation, but side effects. What is the controller 'create' action supposed to do? It's supposed to create a user. Here's how I would test it
# /spec/controllers/users_controller_spec.rb
RSpec.describe UsersController, :type => :controller do
describe "POST 'create'" do
it 'saves new user' do
expect { post :create, name: 'abc' }.to change(User, :count).by(1)
end
end
end
As for mocking and stubbing, I try to stay away from too much stubbing. I think it's super useful when you're trying to test conditionals. Here's an example:
# /app/controllers/users_controller.rb
class UsersController < ApplicationController
def create
user = User.new(user_params)
if user.save
flash[:success] = 'User created'
redirect_to root_path
else
flash[:error] = 'Something went wrong'
render 'new'
end
end
# /spec/controllers/users_controller_spec.rb
RSpec.describe UsersController, :type => :controller do
describe "POST 'create'" do
it "renders new if didn't save" do
User.any_instance.stub(:save).and_return(false)
post :create, name: 'abc'
expect(response).to render_template('new')
end
end
end
Here I'm stubbing out 'save' and returning 'false' so I can test what's supposed to happen if the user fails to save.
Also, the other answers were correct in saying that you want to stub out external services so you don't call on their API every time you're running your test suite.

How to fix my RSpec syntax for version 2.99?

I recently upgraded my Rails 4 application from RSpec 2.X to 2.99 and despite having run Transpec already, some of my tests are still failing.
require 'spec_helper'
describe Invoice, :type => :model do
before :each do
#user = FactoryGirl.create(:user)
#invoice = FactoryGirl.create(:invoice, :user => #user)
end
it "is not open" do
expect {
FactoryGirl.create(:payment, :invoice => #invoice, :amount => 100)
}.to change{#invoice.reload.open?}.from(true).to(false)
end
it "is open" do
expect {
FactoryGirl.create(:payment, :invoice => #invoice, :amount => 99.99)
}.to_not change{#invoice.reload.open?}.to(false)
end
end
The first test passes just like before the RSpec upgrade.
The second test, however, throws an error:
Failure/Error: expect {
`expect { }.not_to change { }.to()` is deprecated.
What must I change my syntax to?
I've already tried a couple of things like not_to, be_falsey etc. Nothing worked so far.
Thanks for any help.
Don't assert that the value doesn't change to something, just assert that it doesn't change:
it "is open" do
expect {
FactoryGirl.create(:payment, :invoice => #invoice, :amount => 99.99)
}.to_not change { #invoice.reload.open? }
end
That doesn't test the initial value of #invoice.reload.open?, but you should have a separate test for that anyway. You don't need to test it again in this test.
Nonetheless, in RSpec 3 you can use .from by itself to test that the value that doesn't change has a given initial value:
it "is open" do
expect {
FactoryGirl.create(:payment, :invoice => #invoice, :amount => 99.99)
}.to_not change { #invoice.reload.open? }.from(false)
end
You can't do that yet in RSpec 2; .to_not change {}.from passes if the value passed to .from is not what is expected. In RSpec 2.99 that causes a warning.

Rspec Test Required parameters

I'm using FactoryGirl and Rspec for my test framework. I have a model that has a validates_presence_of validation on it. The basic Rspec framework includes a test:
describe "with invalid params" do
it "assigns a newly created but unsaved disease as #disease" do
# Trigger the behavior that occurs when invalid params are submitted
Disease.any_instance.stub(:save).and_return(false)
post :create, :disease => {}
assigns(:disease).should be_a_new(Disease)
end
end
Edit:
diseases_controller.rb
# POST /diseases
# POST /diseases.xml
def create
#disease = Disease.new(disease_params)
respond_to do |format|
if #disease.save
format.html { redirect_to(#disease, :notice => 'Disease was successfully created.') }
format.xml { render :xml => #disease, :status => :created, :location => #disease }
else
format.html { render :action => "new" }
format.xml { render :xml => #disease.errors, :status => :unprocessable_entity }
end
end
end
private
def disease_params
params.require(:disease).permit(:name, :omim_id, :description)
end
This test doesn't work with how my application works. Rather than returning a new disease on an incorrect post, it returns an error:
Required parameter missing: disease
Question #1: I don't know how to look at what is being returned with Rspec does the post. The response object doesn't appear to be created in this case? Printing assigns(:disease) doesn't appear to contain anything. I got the error message I posted earlier by submitting a cURL post to the correct URL with empty data (which is what the rspect post should be doing), but I don't know how to get the information of what Rspec is receiving back from the post statement.
Question #2: How do I properly test the response that should be occurring - that it receives an error message saying that a required parameter is missing?
edit:
So my controller seems to indicate that it should render a new disease, but the test fails. If I attempt to submit a disease missing the required parameter on the website, then it does a flash notice that says "Name can't be blank". I'm not sure how to test that in rspec.
edit #2:
Included the code above. disease_params is defined at the bottom of the controller in accordance with recommendations for using the strong_parameters gem.
Thanks!
To answer Question 1 ("I don't know how to look at what is being returned with Rspec does the post")... You can use "puts" statements within your spec (i.e. within the it block). For instance, you can try something like this:
describe "with invalid params" do
it "assigns a newly created but unsaved disease as #disease" do
# Trigger the behavior that occurs when invalid params are submitted
Disease.any_instance.stub(:save).and_return(false)
post :create, :disease => {}
puts :disease
assigns(:disease).should be_a_new(Disease)
end
end
It's a valuable debugging tool. The output will be in the .s and Fs in the terminal when RSpec is running.
For Question 2, I'm not quite sure what you're looking for, but I don't know that you need to (or should) test that the invalid disease is assigned as #disease. I tend to pattern controller specs in the following style (taken from Everyday Rails Testing with RSpec which is where I learned how to write controller specs).
POST create spec example:
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
...
You may have a reason for more thoroughly testing the controller method that I don't know about. In that case, kindly disregard my answer to Question 2 and hopefully my other answer is useful!

Stupefied by RSpec

I'm sorry, but this is beginning to feel like kicking myself in the head. I'm completely baffled by RSpec. Have watched video after video, read tutorial after tutorial, and still I'm just stuck on square one.
=== here is what I'm working with
http://github.com/fudgestudios/bort/tree/master
=== Errors
F
1)
NoMethodError in 'bidding on an item should work'
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.new_record?
spec/controllers/auction_controller_spec.rb:16:
spec/controllers/auction_controller_spec.rb:6:
Finished in 0.067139 seconds
1 example, 1 failure
=== here is my controller action
def bid
#bid = Bid.new(params[:bid])
#bid.save
end
=== here is my test
require File.dirname(__FILE__) + '/../spec_helper'
include ApplicationHelper
include UsersHelper
include AuthenticatedTestHelper
describe "bidding on an item" do
controller_name :items
before(:each) do
#user = mock_user
stub!(:current_user).and_return(#user)
end
it "should work" do
post 'bid', :bid => { :auction_id => 1, :user_id => #user.id, :point => 1 }
assigns[:bid].should be_new_record
end
end
=== spec_helper
http://github.com/fudgestudios/bort/tree/master/spec/spec_helper.rb
It's very disheartening to wake for work at 3 a.m. and accomplish nothing for the day. Please understand.
You've got a couple of things backwards in before(:each). Seeing as the example is specifying that the post should increase the count by 1, you're dealing with real records and there is no reason for stubbing anything at all. Also, at this point, since there is only one example, there is no reason to have a before block. I'd do it this way:
describe ItemsController, "bidding on an item" do
fixtures :users
it "should create a new Bid" do
login_as :quentin
lambda do
post 'bid', :bid => { :auction_id => 1, :user_id => #user.id, :point => 1 }
end.should change(Bid, :count).by(1)
end
end
One thing I'd recommend is creating these things VERY granularly for now until you understand them better. Start with the expectation (post should change bid count), run the spec and let the failure message guide you to add whatever else you need in the spec or in the code.
Jesse,
It'll still pass if you comment out the 2nd two lines of before(:each), which are having no impact on the "should create a new Bid" example.
The lambda keyword creates an arbitrary block of code that is not executed when you define it, but is actually an object you can assign to a variable and execute later:
the_post = lambda do
post 'bid', :bid => { :auction_id => 1, :user_id => #user.id, :point => 1 }
end
At this point that code is not executed, but we can refer to it with the 'the_post' variable. Now we can send it 'should', followed by 'change ...', like this:
the_post.should change(Bid, :count).by(1)
When this line is executed, a few things happen. The material to the right of 'should' is evaluated first, initializing an rspec matcher object with some instructions. That matcher is the argument to 'should' - the equivalent of this:
matcher = change(Bid, :count).by(1)
the_post.should(matcher)
The 'should' method is called on the_post, which is the code block (that still hasn't been executed). Under the hood, the 'should' method passes self (the_post) to the matcher, so the matcher now has everything it needs to evaluate the example.
The matcher calls Bid.count and records the value. Then it executes the block (the_post), and then calls Bid.count a second time and compares it to the value it recorded earlier. In this case, since we're looking for Bid.count to change by 1 (positive is implicit here - increase by 1), if that's what happens the matcher stays silent and the example passes.
If the values are the same, or differ by some value other than 1, the example will fail. You can see that work if you change the expectation to by(2) instead of by(1).
HTH,
David
EDIT: you shouldn't expect Bid.count to increment when using a mock object. Mantra I forgot: caffeine before code.
Just commenting out the lines, for now, so the original is still there.
require File.dirname(__FILE__) + '/../spec_helper'
include ApplicationHelper
include UsersHelper
include AuthenticatedTestHelper
describe "POST to bid_controller" do
controller_name :items
before(:each) do
##bid = mock_model(Bid) # create a new mock model so we can verify the appropriate things
#Bid.stub!(:new).and_return(#bid) # stub the new class method on Bid to return our mock rather than a new ActiveRecord object.
# this separates our controller spec entirely from the database.
end
it "should create a new Bid" do
lambda do
post 'bid', :bid => { :auction_id => 1, :user_id => #user.id, :point => 1 }
end.should change(Bid, :count).by(1)
end
# ... more specs
end
Try to write as small specs as possible, write your setences in such a way as to make it obvious what you should be verifying in that spec. For example, how I changed yours from it "should work" to it "should create a new Bid". If there's more to that controller, write a new spec
for each small piece of functionality.
If you do end up needing mock users, there are some helpers for restful_authentication that make it easier. First create a user fixture in
RAILS_ROOT/spec/fixtures/users.yml, like this:
quentin:
login: quentin
email: quentin#example.com
salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
created_at: <%= 5.days.ago.to_s :db %>
activation_code: 8f24789ae988411ccf33ab0c30fe9106fab32e9b
activated_at: <%= 5.days.ago.to_s :db %>
name: "Quentin"
Then in your spec you will be able to write the following and have your current_user method and all the other parts of restul_authentication
behave as you would expect them to at runtime.
login_as :quentin
# .... the rest of your spec
As an example of a few more specs I might add as a couple more examples:
def do_post
# extracting the method under test, so I don't repeat myself
post 'bid', :bid => { :auction_id => 1, :user_id => #user.id, :point => 1 }
end
it "should create a new Bid" do
lambda do
do_post
end.should change(Bid, :count).by(1)
end
it "should assign the Bid to the proper auction" do
#bid.should_receive(:auction_id=).with(1) # test this, I believe doing Bid.new(params[:bid]) sets the id directly not sets the model
do_post
end
it "should assign the Bid the proper points" do
#bid.should_receive(:point=).with(1)
do_post
end
While I don't quite understand what's going on. (with stubs and the lambda)....
for
def bid
#bid = Bid.new params[:bid]
#bid.save
end
The following passes !!
require File.dirname(__FILE__) + '/../spec_helper'
include ApplicationHelper
include UsersHelper
include AuthenticatedTestHelper
describe "bidding on an item" do
controller_name :items
fixtures :users
before(:each) do
#user = login_as :quentin
#bid = mock_model(Bid) # create a new mock model so we can verify the appropriate things
#bid.stub!(:new).and_return(#bid) # stub the new class method on Bid to return our mock rather than a new ActiveRecord object.
#Bid.stub!(:save).and_return(true)# this separates our controller spec entirely from the database.
end
it "should create a new Bid" do
lambda do
post 'bid', :bid => { :auction_id => 1, :user_id => #user.id, :point => 1 }
end.should change(Bid, :count).by(1)
end
end

Resources