Rspec Test Required parameters - ruby-on-rails

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!

Related

How do I check both rendering :new and JSON response?

I try to add RSpec test for invalid entry create controller action, which responds to HTTP POST verb.
it "case of invalid entry" do
program=FactoryGirl.attributes_for(:program, :faculty_id => faculty.id, :code=>"воруй&убивай")
post :create, {:program=>program, :faculty_shortcut=>faculty }
expect(response).to render_template(:new)
end
Controller:
if #program.save
redirect_to faculty_program_path(faculty, #program)
else
render :new,:json => #program.errors
end
What I get from RSpec
Failure/Error: expect(response).to render_template(:new)
expecting <"new"> but rendering with <[]>
This means that the record has been saved and you have been redirected. If you want to check behaviour when invalid data is passed, do not rely on existing validations as they might change in the future. Just stub valid? method:
it "case of invalid entry" do
program=FactoryGirl.attributes_for(:program)
allow_any_instance_of(Program).to receive(:valid?).and_return false
post :create, {:program=>program, :faculty_shortcut=>faculty }
expect(response).to render_template(:new)
end
Stubbing valid? has this advantage that your test will not change when you change your validations. Validations on their own should be tested within model tests, this way single change in the code won't cause multiple test fails.

Rspec not changing count on create

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.

Table count does not increase after save returns true for nested resource rspec test

I have a 'Mastertag' model as a nested resource for 'Project' with a create action as:
def create
#mastertag = #project.mastertags.build(params[:mastertag])
if #mastertag.save
redirect_to project_mastertags_path, notice: 'Mastertag was successfully created.'
else
render action: "new"
end
end
where #project is initialized in a before filter method.
I have an rspec test as:
describe "POST create" do
context "with valid params" do
it "creates a new Mastertag" do
expect {
post :create, { project_id: #project.id, mastertag: FactoryGirl.attributes_for(:mastertag_without_project) }
}.to change(Mastertag, :count).by(1)
end
end
When I run the test, the #mastertag.save method returns true however the count still remains the same. The test hence fails. This looks pretty strange. Where am I going wrong?
As I was using Mongoid and 'Mastertags' was embedded into Project, there is no separate collection for Mastertags.
I had to change the code to :
describe "POST create" do
context "with valid params" do
it "creates a new Mastertag" do
expect {
post :create, { project_id: #project.id, mastertag: FactoryGirl.attributes_for(:mastertag_without_project) }
}.to change {#project.reload.mastertags.count}.by(1)
end
end
I got help from this Stackoverflow question : RSpec/Mongoid: Expect to change count on embedded models
Check your project_mastertags_path and make sure the redirect after if #mastertag.save works.
also, try replacing if #mastertag.save with if #project.save in your create method.

Rspec testing redirect_to :back

How do you test redirect_to :back in rspec?
I get
ActionController::RedirectBackError:
No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].
How do I go about setting the HTTP_REFERER in my test?
Using RSpec, you can set the referer in a before block. When I tried to set the referer directly in the test, it didn't seem to work no matter where I put it, but the before block does the trick.
describe BackController < ApplicationController do
before(:each) do
request.env["HTTP_REFERER"] = "where_i_came_from"
end
describe "GET /goback" do
it "redirects back to the referring page" do
get 'goback'
response.should redirect_to "where_i_came_from"
end
end
end
From the rails guide when requesting the request with the new request style:
describe BackController < ApplicationController do
describe "GET /goback" do
it "redirects back to the referring page" do
get :show,
params: { id: 12 },
headers: { "HTTP_REFERER" => "http://example.com/home" }
expect(response).to redirect_to("http://example.com/home")
end
end
end
If someone stumbles upon this and they're using request specs, you'll need to explicitly set the headers on the request you're making. The format of the test request depends on which version of RSpec you're using and if you can use keyword arguments instead of positional arguments.
let(:headers){ { "HTTP_REFERER" => "/widgets" } }
it "redirects back to widgets" do
post "/widgets", params: {}, headers: headers # keyword (better)
post "/widgets", {}, headers # positional
expect(response).to redirect_to(widgets_path)
end
https://relishapp.com/rspec/rspec-rails/docs/request-specs/request-spec
IMHO the accepted answer is a bit hacky. A better alternative would be to set the HTTP_REFERER to an actual url in your application and then expect to be redirected back:
describe BackController, type: :controller do
before(:each) do
request.env['HTTP_REFERER'] = root_url
end
it 'redirects back' do
get :whatever
response.should redirect_to :back
end
end
Redirecting to a random string constant feels as if it works by accident
You take advantage of rspec's built in functionality to express exactly what you wanted
You don't introduce and repeat magic string values
For newer versions of rspec, you can use expectations instead:
expect(response).to redirect_to :back
In regard to testing :back links in integration tests, I first visit a deadend page, that I think is not likely to be ever used as a link, and then the page I am testing. So my code looks like this
before(:each) do
visit deadend_path
visit testpage_path
end
it "testpage Page should have a Back button going :back" do
response.should have_selector("a",:href => deadend_path,
:content => "Back")
end
However this does have the flaw that if the link is really to the deadend_path, then the test will incorrectly pass.
request.env['HTTP_REFERER'] = '/your_referring_url'

rspec testing ajax response (should render a partial)

I want to test that my controller action is rendering a partial.
I've poked around and I can't seem to find anything that works.
create action:
def create
#project = Project.new...
respond_to do |format|
if #project.save
format.js { render :partial => "projects/form" }
end
end
end
spec:
it "should save and render partial" do
....
#I expected/hoped this would work
response.should render_partial("projects/form")
#or even hopefully
response.should render_template("projects/form")
#no dice
end
If you're looking for a REAL answer... (i.e. entirely in RSpec and not using Capybara), the RSpec documentation says that render_template is a wrapper on assert_template. assert_template (according to the docs) also indicates that you can check that a partial was rendered by including the :partial key.
Give this a go...
it { should render_template(:partial => '_partialname') }
Update see bluefish's answer below, it seems to be the correct answer
Would you consider using Capybara for your integration testing? I found ajax difficult to test with rspec alone. In your case I'm not even sure you are getting a response back yet. In capybara it waits for the ajax call to finish and you can call the page.has_xxxx to see if it was updated. Here is an example:
it "should flash a successful message" do
visit edit_gallery_path(#gallery)
fill_in "gallery_name", :with => "testvalue"
click_button("Update")
page.has_selector?("div#flash", :text => "Gallery updated.")
page.has_content?("Gallery updated")
click_link "Sign out"
end
another great way to test your ajax controller method is to check the assignments which are later used to render the result. Here is a little example:
Controller
def do_something
#awesome_result = Awesomeness.generete(params)
end
JBuilder
json.(#awesome_result, :foo, :bar)
Rspec Controller Test
describe :do_something do
before do
#valid_params{"foo" => "bar"}
end
it "should assign awesome result" do
xhr :post, :do_something, #valid_params
assigns['awesome_result'].should_not be_nil
end
end

Resources