How to get rspec coverage for the failure of .save - ruby-on-rails

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

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 how to fix malformed request (error code 400)?

I am running RSpec for api, specifically to create posts (I am also working on :update and :destroy, but those two are running fine. I am having trouble with :create)
Here are my RSpec:
describe "POST create" do
before { post :create, topic_id: my_topic.id, post: {title: #new_post.title, body: #new_post.body} }
it "returns http success" do
expect(response).to have_http_status(:success)
end
it "returns json content type" do
expect(response.content_type).to eq 'application/json'
end
it "creates a topic with the correct attributes" do
hashed_json = JSON.parse(response.body)
expect(hashed_json["title"]).to eq(#new_post.title)
expect(hashed_json["body"]).to eq(#new_post.body)
end
end
And here is my create
def create
post = Post.new(post_params)
if post.valid?
post.save!
render json: post.to_json, status: 201
else
render json: {error: "Post is invalid", status: 400}, status: 400
end
end
Here is the error code that I keep getting:
.........F.F....
Failures:
1) Api::V1::PostsController authenticated and authorized users POST create returns http success
Failure/Error: expect(response).to have_http_status(:success)
expected the response to have a success status code (2xx) but it was 400
# ./spec/api/v1/controllers/posts_controller_spec.rb:78:in `block (4 levels) in <top (required)>'
I am really not sure what is wrong with the code. The routes work fine. How can I make the tests pass?
For a better solution tha #spickermann suggested, change post to #post in your #create action and add the code below to your specs and work from there. I bet you have to do something like #post.user = current_user in your controller.
it "#post is valid and have no errors" do
expect(assigns[:post]).to be_valid
expect(assigns[:post].errors).to be_empty
end

rspec devise-confirmable render 'devise/mailer/confirmation_instructions'

I have a devise confirmable on my user model, and I have tests on custom registrations_controller.
One of tests is like
it 'renders :new template' do
post :create, user: valid_params
expect(response).to render_template(:new)
end
But there is error:
Failure/Error: expect(response).to render_template(:new)
expecting <"new"> but rendering with <["devise/mailer/confirmation_instructions"]>
How to get render of #new template?
It seems I got an error by myself. User just not created, so I got this error.

'undefined method' post with Rails and Rspec

Got stuck with:
' undefined method `post' for #<Class:0x000001058c0f68> (NoMethodError)'
on testing controller create action.
I'm using Rails 4, rpsec, and Factory Girl
Controller:
def create
#post = Post.new(post_params)
#post.user_id = current_user.id
if #post.save
flash[:success] = "Yay! Post created!"
redirect_to root_path
else
# flash[:error] = #post.errors.full_messages
render 'new'
end
end
Test:
describe '#create' do
post 'create', FactoryGirl.attributes_for(:post, user: #user)
response.should be_successful
end
I think post method is accessible inside it method block:
describe 'create' do
it 'should be successful' do
post :create, FactoryGirl.attributes_for(:post, user: #user)
response.should be_success
end
end
BTW I think you need to test for redirect, not success status.
Sorry for being off-topic but I just want to give you some advice.
Consider following best practices and use RSpec's expect syntax instead of should. Read more about why the should syntax is a bad idea here: http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
This is how I would rewrite your example:
describe 'create' do
it 'responds with 201' do
post :create, attributes_for(:post, user: #user)
expect(response.status).to eq(201)
end
end
In the example I'm using FactoryGirl's short syntax method attributes_for instead of FactoryGirl.attributes_for, it saves a few bytes. Here's how to make the short methods available (in spec/test_helper.rb):
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
end
I'm testing for the status code 201 which Rails will return by default for a successful create action (redirect should be 3xx).This makes the test more specific.
Hope it's any help for writing better specs.
The issue comes from the fact that post should be used inside an it statement. I usually test my controllers like this:
describe 'POST "create"' do
let(:user) { User.new }
let(:params) { FactoryGirl.attributes_for(:post, user: user) }
let(:action) { post :create, params }
let!(:post) { Post.new }
before do
Post.should_receive(:new).and_return(post)
end
context 'on success' do
before do
post.should_receive(:save).and_return(true)
end
it 'renders success' do
action
expect(response).to be_success
end
it 'redirects' do
action
expect(response).to be_redirected
end
it 'sets flash message' do
action
expect(flash[:success]).to_not be_empty
end
end
context 'on failure' do
before do
post.should_receive(:save).and_return(false)
end
it 'renders new' do
action
expect(response).to render_template(:new)
end
end
end

Rspec testing templates being rendered

Im trying to test a condition where on successful signup a Success Template is rendered by the following controller code
def create
#user = User.new(params[:user])
if #user.save
render :template => "success"
else
flash[:notice] = "Oops Somethings not quite right! :("
render :action => "new"
end
end
I am using the following spec to test out this code
before(:each) do
#user = User.new
#user.attributes = valid_attributes
#params = valid_attributes
#user.stub!(:save).and_return(true)
end
def do_post
post :create
end
it "should create new user " do
count = User.count
do_post
user = User.new(#params)
user.save.should eql(true)
User.count.should eql(count + 1)
end
it "should render the success page on successful signup" do
do_post
#user.save
response.should render_template("success") if #user.save
end
But the example fails "it should render success page on successful signup" with this error message
1)
'UsersController handling POST /users should render the success page on successful signup' FAILED
expected "success", got "users/new.html.erb"
./spec/controllers/users_controller_spec.rb:67:
The success view is an template stored in the views/users/ without an action. Im guessing im making a very fundamental mistake and would like some help .
You are stubbing the #user variable in the test, but the controller will instantiate a new instance so the stub won't be in place.
It's not a good idea to use a stub in this case just to emulate a successful save call. Why don't you supply valid data instead and make sure the action is successful?
The following code is for RSpec > 2.1 and it uses the expect syntax.
before(:each) do
#params = valid_attributes
end
it "should create new user" do
#_before = User.count
post :create, :user => #params
expect(assigns(:user)).to_not be_new_record
expect(User.count).to eq(#_before + 1)
end
it "should render the success page on successful signup" do
post :create, :user => #params
expect(response).to be_successful
expect(response).to render_template("success")
end
Finally, change
render :template => "success"
to
render :action => "success"
For previous RSpec versions or if you have to use the should syntax, use
before(:each) do
#params = valid_attributes
end
it "should create new user" do
#_before = User.count
post :create, :user => #params
assigns(:user).should_not be_new_record
User.count.should == (#_before + 1)
end
it "should render the success page on successful signup" do
post :create, :user => #params
response.should be_successful
response.should render_template("success")
end

Resources