Rails 4 minitest functional test failed for create method - ruby-on-rails

Here's my Code for controller
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
and my code for testing
test "should get create post" do
assert_difference('Post.count') do
post :create, post: {title: #post.title, content: #post.content}
end
assert_redirected_to post_path(assigns{:post})
end
The error that I'm getting is
ArgumentError: comparison of Array with Array failed
test/controllers/posts_controller_test.rb:22:in `block in '
if i remove (assigns{:post}) and try the test
i get this error
ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"posts"} missing required keys: [:id]
Also how to test the if fails render 'new' part in controller.
Thanks

In the following line
assert_redirected_to post_path(assigns{:post})
use parenthesis instead of curly braces. The code should look like this:
assert_redirected_to post_path(assigns(:post))

#post is not instantiated anywhere in the test. You need to pass in params like so
post :create, post: {title: 'title', content: 'content'}

Related

How to get rspec coverage for the failure of .save

In my controller I have the following code:
def create
#post = Post.new(post_params)
if #post.save
flash[:notice] = "#{#post.title} was successfully created."
redirect_to post_path
else
flash[:alert] = #post.errors.full_messages
render :new
end
end
I've managed to write rspec code to cover the true statement within a feature spec, however I am struggling for the false statement. So far this is the rspec I have come up with to solve my problem (placed in posts_controller_spec.rb):
it 'should return false and render the new template' do
allow_any_instance_of(Post).to receive(:valid?).and_return(false)
expect(response).to render_template(:new)
end
Unfortunately, I am met with the following error:
Failures:
1) PostsController create should return
Failure/Error: expect(response).to render_template(:new)
expecting <"new"> but rendering with <[]>
I've tried looking at other questions on stackoverflow as well as other suggestions online, but can't seem to figure it out.
Any help would be much appreciated :)
In your test, you never call the controller.
it 'renders the new template' do
allow_any_instance_of(Post).to receive(:valid?).and_return(false)
post :create, params: {...}
expect(response).to render_template(:new)
end
It would be more direct to do receive(:save).and_return(false).
However, there's no need for a mock here. Send invalid Post parameters.
context 'when the Post params are invalid' do
let(:params) do
{
post: { something: "invalid" }
}
end
it 'does not make a Post, renders the new template, and flashes an error' do
expect {
post :create, params: params
}.to change {
Post.count
}.by(0)
expect(response).to render_template(:new)
expect(flash[:alert]).not_to be_empty
end
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

Update Test On Controller Breaks in Rails 4.1.12

I'm upgrading a Rails site from 4.0 to 4.1.12 and quite a few of my Rspec controller tests are now broken. For example this test broke with the upgrade:
it "update action should render edit template" do
#user = create(:user)
#user.name = "" # model requires name
#controller.stubs(:current_user).returns(#user)
put :update, id: #user
expect(response).to render_template(:edit)
end
I'm getting "No route matches {:action=>"show", :controller=>"accounts", :id=>nil} missing required keys: [:id]". It seems like the update_attributes method is ignoring my model validations all of a sudden.
Controller code:
def update
#user = current_user
if #user.update_attributes(params[:user])
redirect_to user_path(#user), :notice => "Your profile has been updated."
else
render :action => 'edit'
end
end
routes.rb
resources :users do
member do
get 'accounting'
end
collection do
post 'send_password'
end
end
I'm sure I've missed something in the upgrade process but I don't see anything in the docs that's telling me what that is.
So just to put down what I would do - rather than changing the #user object in the test you need to send in the changes you want to make to the user object as params of the request. This way the controller would receive it in the params[] hash - otherwise you aren't really testing what the controller method is doing.
it "with invalid data; update action should render edit template" do
#user = create(:user)
#controller.stubs(:current_user).returns(#user)
patch :update, id: #user, user: { name: '' }
expect(response).to render_template(:edit)
end

How to create DRYer Rspec with Factory Girl

I am building a simple blog app in order to learn BDD/TDD with RSpec and Factory Girl. Through this process, I continue to run into 'Failures' but I believe they have more to do with how I am using Factory Girl than anything.
As you'll see below, in order to get my specs to pass, I'm having a hard time keeping my test DRY - there must be something I am misunderstanding. You'll notice, I'm not using Factory Girl to it's full potential and at times, skipping it altogether. I find that I commonly run into problems when using functions such as get :create, get :show, or put :update within the spec.
I am currently stuck on the #PUT update spec that should simply test the assignment of the #post variable. I have tried multiple types of this spec that I found online, yet none seem to work - hence, is it Factory Girl? Maybe the specs I'm finding online are outdated Rspec versions?
I'm using:
Rspec 3.1.7
Rails 4.1.6
posts_controller_spec.rb
require 'rails_helper'
require 'shoulda-matchers'
RSpec.describe PostsController, :type => :controller do
describe "#GET index" do
it 'renders the index template' do
get :index
expect(response).to be_success
end
it "assigns all posts as #posts" do
post = Post.create(title: 'Charlie boy', body: 'Bow wow wow ruff')
get :index
expect(assigns(:posts)).to eq([post])
end
end
describe '#GET show' do
it 'assigns the request post to #post' do
post = Post.create!(title: 'Charlie boy', body: 'Bow wow wow ruff')
get :show, id: post.id
expect(assigns(:post)).to eq(post)
end
end
describe '#GET create' do
context 'with valid attributes' do
before :each do
post :create, post: attributes_for(:post)
end
it 'creates the post' do
expect(Post.count).to eq(1)
expect(flash[:notice]).to eq('Your post has been saved!')
end
it 'assigns a newly created post as #post' do
expect(assigns(:post)).to be_a(Post)
expect(assigns(:post)).to be_persisted
end
it 'redirects to the "show" action for the new post' do
expect(response).to redirect_to Post.first
end
end
context 'with invalid attributes' do
before :each do
post :create, post: attributes_for(:post, title: 'ha')
end
it 'fails to create a post' do
expect(Post.count).to_not eq(1)
expect(flash[:notice]).to eq('There was an error saving your post.')
end
it 'redirects to the "new" action' do
expect(response).to redirect_to new_post_path
end
end
end
describe '#GET edit' do
it 'assigns the request post to #post' do
post = Post.create!(title: 'Charlie boy', body: 'Bow wow wow ruff')
get :edit, id: post.id
expect(assigns(:post)).to eq(post)
end
end
describe '#PUT update' do
context 'with success' do
before :each do
post :create, post: attributes_for(:post)
end
it 'assigns the post to #post' do
put :update, id: post.id
expect(assigns(:post)).to eq(post)
end
end
end
end
posts_controller.rb
class PostsController < ApplicationController
def index
#posts = Post.all.order('created_at DESC')
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
flash[:notice] = "Your post has been saved!"
redirect_to #post
else
flash[:notice] = "There was an error saving your post."
redirect_to new_post_path
end
end
def show
#post = Post.find(params[:id])
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
# if #post.update(params[:post].permit(:title, :body))
# flash[:notice] = "Your post is updated!"
# redirect_to #post
# else
# flash[:notice] = "There was an error updating your post."
# render :edit
# end
end
private
def post_params
params.require(:post).permit(:title, :body)
end
end
factories/post.rb
FactoryGirl.define do
factory :post do
title 'First title ever'
body 'Forage paleo aesthetic food truck. Bespoke gastropub pork belly, tattooed readymade chambray keffiyeh Truffaut ennui trust fund you probably haven\'t heard of them tousled.'
end
end
Current Failure:
Failures:
1) PostsController#PUT update with success assigns the post to #post
Failure/Error: put :update, id: post.id
ArgumentError:
wrong number of arguments (0 for 1+)
# ./spec/controllers/posts_controller_spec.rb:86:in `block (4 levels) in <top (required)>'
Finished in 0.19137 seconds (files took 1.17 seconds to load)
17 examples, 1 failure
You could definitely leverage factories here.
The factory you've created is actually fine too.
Instead of doing:
post = Post.create(title: 'Charlie boy', body: 'Bow wow wow ruff')
Do this: post = FactoryGirl.create(:post)
You can get ever more DRY if you do this:
# in spec/rails_helper.rb
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
end
This will allow you do this in your spec: post = create(:post)
Regarding your PUT test, try this from a previous SO answer:
describe '#PUT update' do
let(:attr) do
{ :title => 'new title', :content => 'new content' }
end
context 'with success' do
before :each do
#post = FactoryGirl.create(:post)
end
it 'assigns the post to #post' do
put :update, :id => #post.id, :post => attr
#post.reload
expect(assigns(:post)).to eq(post)
end
end
end
Edit:
Also, don't be afraid of moving things in to a before :each do if you need to. They are great at keeping things DRY
The immediate reason why your spec is failing is because you can only call on the controller once per test, and for update you're calling it twice: in the before-action, you are calling create... and then in the main part of the update test you are calling update... controller specs don't like that.
In order to get the existing spec working, you would need to replace the post :create, post: attributes_for(:post) line in the before-action with just creating a post or (as mentioned already) using factory girl to create a post - rather than trying to do it by calling the controller to do it.

Rspec: why is my post :create throwing a :new route not found error?

In a little Rspec test like this:
describe 'POST create' do
context 'with valid attributes' do
#document = FactoryGirl.attributes_for(:document_with_publication)
it 'creates a new document' do
sign_in admin
post :create, document: #document
expect(response).to change(Document, :count).by(1)
end
end
end
I get this error:
DocumentsController user is an administrator POST create with valid attributes creates a new document
Failure/Error: post :create, document: #document
ActionController::RoutingError:
No route matches {:controller=>"documents", :action=>"new", :template=>nil}
Why do I seem to be hitting my :new action and not :create? My routes look like this:
resources :documents, except: [:new, :show]
get 'documents/:template/new', to: 'documents#new', as: :new_templated_document
Thanks. Relevant controller code is here:
def new
#document = current_user.documents.new
#document.template = Template.find(params[:template])
#template_resources = TemplateResources.new(#document, current_user)
end
def create
#document = current_user.documents.new(params[:document])
if #document.save
second_stage_processing
else
redirect_to new_templated_document_path(#document.template), flash:
{ error: 'The document you tried to create was invalid: ' \
"#{#document.errors.full_messages.map { |msg| msg }.join}" }
end
end
I think it is hitting the create action and the #document.save is failing which causes the redirect to redirect_to new_templated_document_path(#document.template) to be called. Since :template is required for that route, but is nil, the route is failing.

Resources