Testing Create Action in Rails Controller - ruby-on-rails

Testing Rails 4 Application with RSpec 3 throws an Argument Error (2 for 0) when running a test on posts#create.
My controller:
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to(#post,
:notice => 'Post was successfully created.') }
format.json { render :json => #post,
:status => :created, :location => #post }
else
format.html { render :action => "new" }
format.json { render :json => #post.errors,
:status => :unprocessable_entity }
end
end
end
private
def post_params
params.require(:post).permit(:title, :body)
end
My Routes:
resources :posts
My Test:
describe 'POST posts#create' do
it 'allows admin to create a new post' do
sign_out user
sign_in admin
post :create, post: { title: 'Title', body: 'Body' }
expect(response).to have_http_status(:success)
end
end
The problem is on the post :create... line of the controller test. I'm unsure of why it is not running correctly. Solutions I have attempted:
1 post :create, post: title: 'Title', body: 'Body'
Thinking there was something wrong about my syntax. This however, throws an error.
2 post :create, { title: 'Title', body: 'Body' }
I thought that the post might be implicit since it is the name of the controller. No dice.
3 post :create, post:(post_params)
Again, I thought that because I have set up the usual permitted params, that they would be necessary. Not surprisingly, the test doesn't have post_params in its current scope.
4 post :create, post: { admin: { title: 'Title', body: 'Body' } }
I had before read that you have to pass in the user creating the post item. Incorrect.
5 post :create=> { title: 'Title', body: 'Body' }
I thought it might be the case that posting implicitly creates a post because that is the controller. However, it returns the argument error.
6 post :post=> { title: 'Title', body: 'Body' }
Finally, I thought that I have to post a new post a give the parameters without having to use the create action.
Edit - 7 post(:create, post: { title: 'Title', body: 'Body' })
After reviewing the API docs (http://api.rubyonrails.org/classes/ActionController/TestCase.html), I thought this would work but it's still the same argument error.
8
describe 'POST admin#create' do
it 'allows admin to create a new post' do
sign_in admin
expect{
post :create, post: {title: 'Title', body: 'Body' }
}.to change(Post, :count).by(1)
end
end
I attempted to use expect as a block thinking that it would pick up the arguments. Nope.
Compromise Solution
describe 'POST admin#create' do
it 'allows admin to create a new post' do
sign_in admin
expect{
Post.create({title: 'Title', body: 'Body' })
}.to change(Post, :count).by(1)
end
end
I think the name of the action being the name of the controller threw RSpec off. So, instead I just manually created a post. Probably not the recommended solution.
None of these worked. What am I missing?
Error Output
Failure/Error: post(:create, post: { title: 'Title', body: 'Body' })
ArgumentError:
wrong number of arguments (2 for 0)
# ./spec/controllers/posts_controller_spec.rb:101:in `block (3 levels) in <top (required)>'
I have also taken off all of my before_filters in order to see if that was the problem, however, it gives the same error.

Your syntax looks correct. It would look like this:
describe 'POST admin#create' do
it 'allows admin to create a new post' do
sign_out user
sign_in admin
post(:create, {post: {title: 'Title', body: 'Body'}})
expect(response).to have_http_status(:success)
end
end
...but the parens and outer curlys are optional.
The error is not coming from your test. The error occurs during the post to :create. It is coming from inside the controller or the model during the :create request.

Related

Testing requests that have redirects in RSpec

I'm trying to test a post request that has a redirect if successful:
class PostsController < ApplicationController
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
else
render :new
end
end
end
I was wondering if it were at all possible to test to see if I receive a 201 response code before the redirect. Here is how I currently have my code. It will err because the redirect happens first:
RSpec.describe 'Posts', type: :request do
describe 'POST #create' do
it 'has a 201 response code' do
post posts_path, params: { post: valid_attributes }
expect(response).to have_http_status(201)
end
end
end
You can check that a post was created and that a user was redirected, if params are valid. And if you have any validation in the Post model, it is good idea to test invalid params:
RSpec.describe 'PostsController', type: :request do
describe 'POST #create' do
context 'with valid params' do
it 'creates a new post' do
expect { post posts_path, params: { post: valid_attributes } }.to change(Post, :count).by(1)
expect(response).to redirect_to post_path(Post.last)
end
end
context 'with invalid params' do
it 'does not create a new post' do
expect { post posts_path, params: { post: invalid_attributes } }.not_to change(Post, :count)
expect(response).to have_http_status 200
end
end
end
end
You're response code will be a 302 since the post is being created successfully. In the example code you've been given you won't get a 201 back. You could check that you don't receive a 201
expect(response).to_not have_http_status(201).
Creating a new Post model doesn't return an HTTP status code. It creates a row in the database. If you want to check that a post was created you could check that the count of posts is 0 at the start of the test and 1 at the end.

Rails Controller Testing: ActionController::UrlGenerationError: No route matches

I’m teaching myself to write controller tests and am getting this getting this error:
ERROR["test_should_update_post", PostsControllerTest, 2015-10-11 12:12:31 -0400]
test_should_update_post#PostsControllerTest (1444579951.69s)
ActionController::UrlGenerationError: ActionController::UrlGenerationError: No route matches {:action=>"update", :controller=>"posts", :post=>{:title=>"My Post", :body=>"Updated Ipsum"}}
test/controllers/posts_controller_test.rb:51:in `block (2 levels) in <class:PostsControllerTest>'
test/controllers/posts_controller_test.rb:50:in `block in <class:PostsControllerTest>’
This is my test:
test "should update post" do
assert_difference('Post.count') do
put :update, post: {title: 'My Post', body: 'Updated Ipsum'}
end
assert_redirected_to post_path(assigns(:post))
end
this is my yaml:
entry_one:
title: "Foobar"
body: "Ipsum This"
entry_two:
title: "Barfoo"
body: "This Ipsum"
and this is my controller:
def update
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post, notice: 'Event updated successfully'
else
render :edit
end
end
Can you point me towards the problem I need to solve?
I can tell from the error and the line count that it’s something to do with the lines:
assert_difference('Post.count') do and put :update, post: {title: 'My Post', body: 'Updated Ipsum’}
You need to pass an id to the update action:
put :update, id: <THE ID HERE>, post: {title: 'My Post', body: 'Updated Ipsum'}
According to your update action in your controller, you need to pass an id of the post in your params.
So, in your test, build your params hash like this:
let(:update_query_parameters) { { post: { title: 'My Post', body: 'Updated Ipsum' }, id: post.id } }
Then, use update_query_parameters to pass as params for your put :update method:
test "should update post" do
assert_difference('Post.count') do
put :update, update_query_parameters
end
assert_redirected_to post_path(assigns(:post))
end
Thanks to two the commenters above, I was able to understand the problem I needed to solve: That I need to pass an id in my update test.
I'd already done this in a similar edit test for the same app, i knew exactly what to try.
I'd previously used a setup method in my test to pass my yaml shared above into my tests:
def setup
#post = posts(:entry_one)
end
With this method I can pass #post.id into my update test and get it to pass as such:
test "should update post" do
assert_no_difference('Post.count') do
put :update, id: #post.id, post: {title: 'My Post', body: 'Updated Ipsum'}
end
assert_redirected_to post_path(assigns(:post))
end

Can't fix ProductsController PUT update errors

I have a problem with workshop app that i'm doing right now. I can't fix two last errors from tests. In my opinion application is working fine in browser. Maybe there is something wrong with tests? Any help would be appreciated.
Two errors:
1) ProductsController PUT update with valid params updates the requested product
Failure/Error: Unable to find matching line from backtrace
Exactly one instance should have received the following message(s) but didn't: update
2) ProductsController PUT update with invalid params re-renders the 'edit' template
Failure/Error: response.should render_template("edit")
expecting <"edit"> but rendering with <[]>
Test code:
require 'spec_helper'
describe ProductsController do
let(:category) { create(:category) }
let(:valid_attributes) { { "title" => "MyString", "category_id" => category.id, "price" => 5.59,
"description" => "Lorem ipsum dolor sit amet"} }
let(:valid_session) { {} }
describe "PUT update" do
let(:user) { build(:user) }
before do
sign_in user
controller.stub(:user_signed_in?).and_return(true)
controller.stub(:current_user).and_return(user)
controller.stub(:authenticate_user!).and_return(user)
end
describe "with valid params" do
it "updates the requested product" do
product = Product.create! valid_attributes
Product.any_instance.should_receive(:update).with({ "title" => "MyString" })
put :update, { id: product.to_param, product: { "title" => "MyString" }, category_id:
category.to_param }, valid_session
end
describe "with invalid params" do
it "re-renders the 'edit' template" do
product = Product.create! valid_attributes
Product.any_instance.stub(:save).and_return(false)
put :update, { id: product.to_param, product: { "title" => "invalid value" }, category_id:
category.to_param }, valid_session
response.should render_template("edit")
end
end
end
end
ProductsController#update code:
def update
if self.product.update(product_params)
redirect_to category_product_url(category, product), notice: 'Product was successfully
updated.'
else
render action: 'edit'
end
end
In general
expecting <"edit"> but rendering with <[]>
Typically this means that you are expecting a render (following a validation failure for example) and your controller perform a redirect (after a successful saving of your model)
In you code
You stub the save method here:
Product.any_instance.stub(:save).and_return(false)
But call an action that uses the update method
if self.product.update(product_params)
So the action succeed -> your controller redirects -> your 'edit' template is not rendered -> your spec fails
Your solution
Instead of stubbing save, you should stub valid?, it's the good practice in that case
Product.any_instance.stub(:valid?).and_return(false)

Testing 'create' action of a nested resource with Rspec + FactoryGirl

I have searched a lot on the internet as well as other similar questions on Stackoverflow, however I am still not sure on how to test the create method of a nested resource in my rails application.
The resource routes
resources :projects, :except => [:index, :show] do
resources :mastertags
end
Here is the action I want to test :
def create
#mastertag = #project.mastertags.build(params[:mastertag])
respond_to do |format|
if #mastertag.save
format.html { redirect_to project_mastertags_path, notice: 'Mastertag was successfully created.' }
else
format.html { render action: "new" }
end
end
end
Here is my corresponding Rspec test:
context "with valid params" do
it "creates a new Mastertag" do
project = Project.create! valid_attributes[:project]
mastertag = Mastertag.create! valid_attributes[:mastertag]
expect {
post :create, { project_id: project.id, :mastertag => valid_attributes[:mastertag] }
}.to change(Mastertag, :count).by(1)
end
end
I have a valid_attributes function as :
def valid_attributes
{ :project => FactoryGirl.attributes_for(:project_with_researcher), :mastertag => FactoryGirl.attributes_for(:mastertag) }
end
I get the following error :
Failure/Error: post :create, { project_id: project.id, :mastertag => valid_attributes[:mastertag] }
NoMethodError:
undefined method `reflect_on_association' for "5168164534b26179f30000a1":String
I also tried a couple of variations but nothing seems to work.
The answer will change slightly on your version of FactoryGirl.
The first question is, where is #projet getting created? somewhere else I guess?
You're creating both the project and the mastertag, why are you doing this?
project = Project.create! valid_attributes[:project]
mastertag = Mastertag.create! valid_attributes[:mastertag]
This is exactly what FactoryGirl does when you call Factory(:project) and Factory(:mastertag)
The next "wat", is that you create a mastertag in your spec at all. You don't use that variable anywhere. Without fixing your problem, your spec would look a lot better like this:
it "creates a new Mastertag" do
project = Factory(:project)
expect {
post :create, { project_id: project.id, :mastertag => Factory.attributes_for(:mastertag)}
}.to change(Mastertag, :count).by(1)
end
Okay, so now that we're done cleaning up the spec, lets look at your error.
Looks like it's in this line
format.html { redirect_to project_mastertags_path, notice: 'Mastertag was successfully created.' }
This path needs a project id.
format.html { redirect_to project_mastertags_path(#project), notice: 'Mastertag was successfully created.' }
#John Hinnegan's Answer is absolutely correct. i just want to add that is important, to use on the Project the Id, and not just project:
Sometimes it could be obvious to use project: in the param, but this do not work.
Works:
expect {
post :create, { project_id: project.id, :mastertag => valid_attributes[:mastertag] }
}.to change(Mastertag, :count).by(1)
Does not work:
expect {
post :create, { project: project.id, :mastertag => valid_attributes[:mastertag] }
}.to change(Mastertag, :count).by(1)

RSpec Newbie: Devise/Cancan causing otherwise working controller spec to fail

I'm trying to get an RSpec controller spec to pass. It's almost identical to the scaffold-generated spec, except a user is signed into devise first. If I disable 'load_and_authorize_resource' from the controller (which checks permissions), everything works fine. But if I put the line back in, it fails with:
1) PostsController logged in administrator POST create with valid params assigns a newly created post as #post
Failure/Error: post :create, :post => {'title' => 'test title'}
<Post(id: integer, title: string, cached_slug: string, content: text, user_id: integer, created_at: datetime, updated_at: datetime) (class)> received :new with unexpected arguments
expected: ({"title"=>"test title"})
got: (no args)
# ./spec/controllers/posts_controller_spec.rb:52:in `block (5 levels) in <top (required)>'
I had assumed the spec wasn't logging in the user correctly, but a puts current_user.role.name confirms the user is logged in correctly, and has the necessary role. Performing the actual process in a browser confirms it works as desired.
Anyone have any suggestions? I'm quite stumped. Controller below:
def create
#post = Post.new(params[:post])
#post.user = current_user
respond_to do |format|
if #post.save
flash[:notice] = "Post successfully created"
format.html { redirect_to(#post)}
format.xml { render :xml => #post, :status => :created, :location => #post }
else
format.html { render :action => "new" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end
end
...And the spec
describe "with valid params" do
it "assigns a newly created post as #post" do
Post.stub(:new).with({'title' => 'test title'}) { mock_post(:save => true) }
post :create, :post => {'title' => 'test title'}
assigns(:post).should be(mock_post)
end
...And supporting stuff in the spec:
before(:each) do
#user = Factory(:admin)
sign_in #user
end
def mock_post(stubs={})
#mock_post ||= mock_model(Post, stubs).as_null_object
end
Many thanks...
Try upgrading CanCan to version 1.5. I had the issue earlier but I think it went away when I upgraded.

Resources