How to test 'new' controller actions? - ruby-on-rails

I am using Ruby on Rails 3.2.2, Rspec 2.9.0 and RspecRails 2.9.0. I am trying to test a new controller action and I would like to know why I get the error explained above only for that action.
Given:
# controller
class ArticlesController < ApplicationController
before_filter :signed_in
def new
#article = Article.new
# This is just a sample code line to show you where the error happens?
#article.new_record?
...
end
def show
#article = Article.find(params[:id])
...
end
end
# spec file
require 'spec_helper'
describe ArticlesController do
before(:each) do
#current_user = FactoryGirl.create(:user)
# Signs in user so to pass the 'before_filter'
cookies.signed[:current_user_id] = {:value => [#current_user.id, ...]}
end
it "article should be new" do
article = Article.should_receive(:new).and_return(Article.new)
get :new
assigns[:article].should eq(article)
end
it "article should be shown" do
article = FactoryGirl.create(:article)
get :show, :id => article.id.to_s
assigns[:article].should eq(article)
end
end
When I run the Example related to the new action I get this error (it is related to the #article.new_record? code line in the controller file):
Failure/Error: get :new
NoMethodError:
undefined method `new_record?' for nil:NilClass
But when I run the Example related to the show action it passes without errors.
What is the problem? How can I solve that?

The problem is the way you've done
Article.should_receive(:new).and_return(Article.new)
This is the same as
temp = Article.should_receive(:new)
temp.and_return(Article.new)
So by the time you are setting up the return value, Article.new has already been mocked out and so returns nil, so you're doing and_return(nil) Create the return value first, i.e.
new_article = Article.new #or any other way of creating an article - it may also be appropriate to return a mock
Article.should_receive(:new).and_return(new_article)

Try:
it "article should be new" do
article = FactoryGirl.build(:article)
Article.stub(:new).and_return(article)
get :new
assigns(:article).should == article
end

Related

RSpec ActiveRecord::RecordNotFound Couldn't find User with 'id'=

There are many answered questions about this topic but I can't seem to apply the answers to my issue. I'm getting the following error:
ItemsController POST create redirect to show page
Failure/Error: #user = User.find(params[:user_id])
ActiveRecord::RecordNotFound:
Couldn't find User with 'id'=
I know the reason for the error is because the user_id is nil but I can't seem to figure out the reason why. I'm still fairly new to Rails so excuse me if this is a really easy fix.
RSpec Test
require 'rails_helper'
RSpec.describe ItemsController, type: :controller do
describe "POST create" do
it "redirect to show page" do
post :create, user_id: #user, item: { name: "name"}
expect(response).to redirect_to(user_show_path)
end
end
end
Items Controller
class ItemsController < ApplicationController
def create
#user = User.find(params[:user_id])
#item = Item.new(item_params)
#item.user = current_user
if #item.save
#item = Item.update_items(params[:items])
redirect_to current_user, notice: "Item was saved successfully."
else
flash[:error] = "Error creating item. Please try again."
render user_show_path
end
end
private
def item_params
params.require(:item).permit(:name)
end
end
Routes
user_items POST /users/:user_id/items(.:format) items#create
...and I've implemented Devise for the user part. Thanks in advance for the help!
You need to create #user before using it. You can use factory_girl gem or directly do
#user = User.create
post :create, user_id: #user.id, item: { name: "name"}
Hope this works

Rspec, how do I test if a response renders a template

I'm new to rspec and writing test. I need to write a test for the show action of my VideosController. Here is my test
describe VideosController do
describe "GET show" do
it "renders the show template" do
video = Video.create(title: "Game of Thrones", description: "A very awesome show!")
get :show, id: video.id
expect(response).to render_template(:show)
end
end
end
When I run this I get this error
1) VideosController GET show renders the show template
Failure/Error: expect(response).to render_template(:show)
expecting <"show"> but rendering with <[]>
What am I missing here?
EDIT: VideosController
class VideosController < ApplicationController
before_action :require_user
def show
#video = Video.find(params[:id])
end
def search
#search_phrase = params[:search_term]
#search_result = Video.search_by_title(#search_phrase)
end
end
For setting the user, controller tests are meant to be isolated from other aspects of the app. So really you can simply stub the user. Provided you're using a method in your application controller like:
def current_user
#current_user ||= User.find session[:user_id]
end
Then you can just stub it:
controller.stub(current_user: User.create(email: "test#example.com", password: "abc123", password_confirmation: "abc123"))
Adjust the user params to your need. Also instead of invoking active record directly, have a look at: http://github.com/thoughtbot/factory_girl.
So what happens there is when the controller invokes current_user, it returns the user record.

Why is instance variable nil in my rails partial?

I have a user model (agents) in my app who can create posts. They can do this from their dashboard. I also show all their existing posts within their dashboard. The problem comes when trying to create a new post with no content which I'm testing to make sure it renders an error. Creating a post with content works but creating one with out keeps triggering an error which says my #posts instance variable is nil. Not sure if I missed something?
Dashboard view:
.container
.column.span9
- if current_agent
= render 'home/shared/agent_post_panel'
= render 'home/shared/agent_dashboard_tabs'
agent_dashboard_tabs:
.tabs-container
#posts
.content
- if #posts.any? //This is where the error is triggered
= render partial: 'shared/post', collection: #posts
Controller for Dashboard:
class HomeController < ApplicationController
before_filter :authenticate!, only: [:dashboard]
def index
end
def dashboard
if current_agent
#post = current_agent.posts.build
#posts = current_agent.posts
end
end
end
My post controller:
class PostsController < ApplicationController
before_filter :authenticate_agent!
def create
#post = current_agent.posts.build(params[:post])
if #post.save
flash[:notice] = "Post created!"
redirect_to dashboard_path
else
flash[:error] = "Post not created"
render 'home/dashboard'
end
end
end
Tests:
feature 'Creating posts' do
let(:agent) { FactoryGirl.create(:agent) }
before do
sign_in_as!(agent)
visit dashboard_path
end
scenario "creating a post with valid content" do
fill_in 'post_content', :with => 'I love donuts'
expect { click_button "Post" }.to change(Post, :count).by(1)
end
scenario "creating a post with invalid content" do
expect { click_button "Post" }.not_to change(Post, :count)
click_button "Post"
page.should have_content("Post not created")
page.should have_content("can't be blank")
end
You are rendering home/dashboard from within posts#create when the post has errors, but you are not setting #posts variable.
You should add this before render 'home/dashboard'
#posts = current_agent.posts
Use redirect_to dashboard_path instead of render 'home/dashboard'.
and you can keep flash message on dashboard by calling keep method of flash in home#dashboard.
flash.keep
If I understand your question correctly, You are having an issue in the loading the page (HomeController#index)
As I can see prior to load your index action, you check if the user is logged in or not,
make sure your application flow goes into
if current_agent
#make sure you application flow comes here
#post = current_agent.posts.build
#posts = current_agent.posts
end
If not you might want to slightly modify this flow as (if this fits your requirment)
if current_agent
#post = current_agent.posts.build
#posts = current_agent.posts
else
#post = Post.new
#posts = []
end
and finally its always good to use try, when you are not sure about the object you get
if #posts.try(:any?)

testing "create" method in ruby with rspec

I have written this controller code in Ruby on Rails
class PostsController < ApplicationController
before_filter :authenticate_user!
def index
#posts = Post.all(:order => "created_at DESC")
respond_to do |format|
format.html
end
end
def create
#post = Post.create(:message => params[:message])
respond_to do |format|
if #post.save
format.html { redirect_to posts_path }
format.js
else
flash[:notice] = "Message failed to save."
format.html { redirect_to posts_path }
end
end
end
end
and corresponding to this I have written the following test case :-
require 'spec_helper'
describe PostsController do
describe "GET 'index'" do
it "returns http success" do
get 'index'
response.should be_success
end
end
describe "#create" do
it "creates a successful mesaage post" do
#post = Post.create(message: "Message")
#post.should be_an_instance_of Post
end
end
end
I am getting failures on both. Please take a look on the code and help me figure out.
I suspect you are not logged in since you are using Devise?
Maybe you need to include the devise testhelpers:
describe PostsController do
include Devise::TestHelpers
before(:each) do
#user = User.create(...)
sign_in #user
end
#assertions go here
end
As Tigraine states, it appears as though you probably are not logged in (with Devise) when the tests get executed. However, showing the failures would help in narrowing down the problem further.
On top of that, the second test isn't really an integration test and I would probably prefer something like the following to test the same condition. There are two types of test you could do:
# inside 'describe "#create"'
let(:valid_params) { {'post' => {'title' => 'Test Post'} }
it 'creates a new Post' do
expect {
post :create, valid_params
}.to change(Post, :count).by(1)
end
# and / or
it 'assigns a new Post' do
post :create, valid_params
assigns(:post).should be_a(Post)
assigns(:post).should be_persisted
end
Don't forget to add this line into your spec_helper.rb
require "devise/test_helpers"
include Devise::TestHelpers
Nevertheless, here is link for Devise wiki - How to test Controllers where you can find more info about this approach. I recommend writing the before method without (:each), what I remember it sometimes causes problems.
before do
#user = FactoryGirl.create(:user)
sign_in #user
end
Can always use:
puts response.inspect
To see how your response looks like.

Why is my Ruby on Rails RSpec test passing with both #plugin and #plugins?

I'm in Chapter 10 of the Foundation Rails 2 book. We're working with RSpec.
We're testing the 'index' action of the 'PluginsController'.
Here's the code for the controller:
class PluginsController < ApplicationController
# GET /plugins
# GET /plugins.xml
def index
#plugins = Plugin.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #plugins }
end
end
Here's the code for the tests for that controller:
require File.dirname(__FILE__) + '/../spec_helper'
describe PluginsController, " GET to /plugins" do
before do
#plugin = mock_model(Plugin)
Plugin.stub!(:find).and_return([#plugin])
end
it "should be successful" do
get :index
response.should be_success
end
it "should find a list of all plugins" do
Plugin.should_receive(:find).with(:all).and_return([#plugin])
get :index
end
it "should assign the list of plugins to a variable to be used in the view"
it "should render the index template"
end
When we were writing our test, I thought that this line
Plugin.should_receive(:find).with(:all).and_return([#plugin])
should have had
#plugins
and not
#plugin
because in the controller we have
def index
#plugins = Plugin.find(:all)
I wanted to see what would happen if I changed
Plugin.should_receive(:find).with(:all).and_return([#plugin])
to
Plugin.should_receive(:find).with(:all).and_return([#plugins])
and the test passed.
So...why is it #plugin and not #plugins? And...why does the test pass with both?
Cheers!
When you have an undefined variable, as #plugins is since you don't define it earlier in the test, it will show up as nil.
What Plugin.should_receive(:find).with(:all).and_return([#plugins]) actually do in this case is that it tells Plugin.find(:all) to return [nil], which according to your test is valid.

Resources