rspec converting repeat block into method - ruby-on-rails

I have this context in my spec file.
context 'get :index' do
it 'should be loaded successfully if current user is not customer' do
sign_in #creative
get :index
response.should be_success
end
it 'should redirect to root page if current user is customer' do
sign_in #customer
get :index
response.should redirect_to root_path
end
end
context 'post :create' do
it 'should be loaded successfully if current user is not customer' do
sign_in #creative
post :create
response.should be_success
end
it 'should redirect to root page if current user is customer' do
sign_in #customer
post :create
response.should redirect_to root_path
end
end
I repeat the same code in two different context.I convert it like this method but it doesn't work.
def check_user_sign_in(request_type, action)
context '#{request_type} :#{action}' do
it 'should be loaded successfully if current user is not customer' do
sign_in #creative
request_type action
response.should be_success
end
it 'should redirect to root page if current user is customer' do
sign_in #customer
request_type action
response.should redirect_to root_path
end
end
end
end
Here the problem is I didn't use parameter as a method name.
Do you know how can I use it with dry way?

This:
context '#{request_type} :#{action}' do
will not work because string interpolation is not evaluated inside single quotes.
You must use double quotes:
a = 'alpha' #=> "alpha"
b = 'beta' #=> "beta"
'#{a} #{b}' #=> "\#{a} \#{b}"
"#{a} #{b}" #=> "alpha beta"

Related

Rspec check the template after a redirect

Here is my create action for users:
def create
#user = User.new(user_params)
if #user.save
respond_to do |format|
format.html {
redirect_to edit_admin_user_path(#user)
flash[:success] = "Successfully created"
}
end
else
render :new
flash[:alert] = "Something went wrong"
end
end
My test is looking like this:
context "POST methods" do
describe "#create" do
it "renders the edit template" do
post :create, user: FactoryGirl.attributes_for(:user)
expect(response).to render_template(:edit)
end
end
end
However I'm getting this error:
Failures:
1) Admin::UsersController POST methods #create renders the edit template
Failure/Error: expect(response).to render_template(:edit)
expecting <"edit"> but was a redirect to <http://test.host/admin/users/80/edit>
I want to check if the edit.html.haml file is rendered after creating a user. What am I doing wrong?
Update #1
I do check for redirect in another test, this is my full test suite:
require 'rails_helper'
RSpec.describe Admin::UsersController, type: :controller do
render_views
context "POST methods" do
describe "#create" do
it "using valid params" do
expect{
post :create, user: { email: "something#hello.com", password: "long12345678" }
}.to change(User, :count).by(1)
# get user_path('1')
end
it "redirects to the edit page after saving" do
post :create, user: FactoryGirl.attributes_for(:user)
user = User.last
expect(response).to redirect_to(edit_admin_user_path(user.id))
end
it "renders the edit template" do
post :create, user: FactoryGirl.attributes_for(:user)
user = User.last
expect {
redirect_to(edit_admin_user_path(user.id))
}.to render_template(:edit)
end
context "it redirects to new" do
it "if user has no valid mail" do
post :create, user: { email: "something", password: "long12345678" }
expect(response).to render_template(:new)
end
it "if user has no valid password" do
post :create, user: { email: "something#mail.com", password: "short" }
expect(response).to render_template(:new)
end
end
end
end
end
What I want is to actually check if the edit template is rendered. Because with expect(response).to redirect_to(edit_admin_user_path(user)) it does not check the template. This test passes even if I have no edit.html.haml file at all.
When you're testing create action you should just check correctness of redirect. In this action you're not actually rendering edit template, but you're just making redirect to the edit path of created entity. So this is the thing you should check.
describe "#create" do
it "redirects to the edit path" do
post :create, user: FactoryGirl.attributes_for(:user)
expect(response).to redirect_to(edit_admin_user_path(User.last))
end
end
Then you should have another test for edit action, where you're checking template rendering. That will mean that after redirect in create action you also will see the proper template.
You are redirecting to edit_admin_user_path after successfully saving the User in your controller action. But, you're testing render in the test instead.
Update your test as below.
context "POST methods" do
describe "#create" do
before(:each) do
#user = FactoryGirl.attributes_for(:user)
end
it "renders the edit template" do
post :create, user: #user
expect(response).to redirect_to(edit_admin_user_path(#user))
end
end
end

'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 shared_examples_for method

I have this controller spec file.
describe BriefNotesController do
render_views
before(:all) do
#customer=Factory(:customer)
#project=Factory(:project_started, :owner => #customer)
end
context 'get :new' do
it 'should redirect to login page for not signed in users' do
get :new, :project_id => #project.id
response.should new_user_session_path
end
end
context 'get :create' do
it 'should redirect to login page for not signed in users' do
post :create, {:project_id => #project.to_param, :brief_note => Factory.attributes_for(:brief_note, :project_id => #project.id)}
response.should redirect_to new_user_session_path
end
end
In these tests I repeat the same authentication test for each action and thus I want to recode it with dry way.I tried to recode this test with shared_examples_for method but I didn't handle with it.
Something like this can work
shared_examples_for 'redirects to login page if not signed in' do
context 'get :new' do
it 'should redirect to login page for not signed in users' do
get :new, :project_id => resource.id
response.should redirect_to new_user_session_path
end
end
context 'get :create' do
it 'should redirect to login page for not signed in users' do
post :create, resource_params
response.should redirect_to new_user_session_path
end
end
end
You would use it like this:
describe BriefNoteController do
let(:customer) { Factory(:customer) }
let(:project) { Factory(:project_started, :owner => customer) }
let(:resource) { project }
let(:resource_params) do
{
:project_id => project.to_param,
:brief_note => Factory.attributes_for(:brief_note, :project_id => project.id)
}
end
it_behaves_like 'redirects to login page if not signed in'
end

Rspec POST controller create test fails but web based submission works

I have the following rspec test for create method in my
describe "with valid information" do
it "should respond with success" do
post 'create', :show_secretary_id => #show_secretary.id, :show => #show
response.should be_success
end
it "should incremenet the show count" do
expect do
post 'create', :show_secretary_id => #show_secretary.id, :show => #show
end.to change(Show,'count').by(1)
end
end
The test fails. However, when I try the create method in the browser, it works. Any ideas on what I am missing?
EDIT: My Controller Code
def create
#show_secretary = ShowSecretary.find_by_id(params[:show_secretary_id])
#show = #show_secretary.shows.build(params[:show])
if #show.save
flash[:notice] = "Successfully created show"
redirect_to show_path #show
else
render 'new'
end
end
EDIT: #show_secretary, #show
These two objects are ActiveRecords created and built by FactoryGirl respectively.
#show_secretary = FactoryGirl.create(:show_secretary_user).verifiable
#show = FactoryGirl.build(:show)
Replace
#show = FactoryGirl.build(:show)
with:
#show = FactoryGirl.attributes_for(:show)

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