Pass instance variable to controller test in rspec? - ruby-on-rails

I'm adding to test to an existing project. I have a user class that is assigned a unique url on create ad then is redirected to a path that contains that url.
I'm following the rspec book and wrote this test
it 'redirects to users#show' do
post :create, user: attributes_for(:guest)
expect(response).to redirect_to guest_path(:guest)
end
I get this back when I run the test.
Expected response to be a redirect to <http://test.host/guest/guest> but was a redirect to <http://test.host/guest/h7gutr1CMYxa5VNgWH5l1A>
The redirect the controller is sending back is correct but I am obviously writing the test incorrectly. How can I alter the expectation to pass the test?
Edit: Got a little closer, but still not there. Using
expect(response).to redirect_to guest_path(assigns(:user))
gives:
Expected response to be a redirect to <http://test.host/guest/1> but was a redirect to <http://test.host/guest/zaOVsiPBwQ4yNnC0mJNoNA>
using:
expect(response).to redirect_to guest_path(assigns(:user))
gives:
NoMethodError:
undefined method `url' for :user:Symbol
Solution:
I couldn't figure out how to call methods on the instance variable. It's done like this:
it 'redirects to users#show' do
post :create, user: attributes_for(:guest)
expect(response).to redirect_to guest_path(assigns(:user).url)
end

Try this:
it 'redirects to users#show' do
post :create, user: attributes_for(:guest)
expect(response).to redirect_to guest_path(assigns(:guest))
end
In this case, assigns(:guest) will be equal to the instance variable with the same name as the arguments (:guest) that was returned by the request you're testing for.

Related

How can I simulate a session login to test Rails controller methods?

I'm trying to test some Rails controller actions that require a login. Right now logging in is managed by setting the logged in user to session[:user_id] which all works as expected, but when I try to manually set it in my test (definitely open to a better way!) I get NoMethodError: undefined method session' for nil:NilClass` which is a really confusing error. message in the first place--I would have expected a NameError for an undefined variable. I'd like to test both logged in and non logged in methods so I'm not sure if logging in during the setup phase is accurate (although I couldn't get it working anyway) but I'm open to any and all suggestions. This is for a lesson teaching testing, so I want to keep it basic and please point out if I'm doing anything super wrong here before I teach it to impressionable youth.
require 'test_helper'
class TacosControllerTest < ActionDispatch::IntegrationTest
setup do
#meat = tacos(:meat)
#veggie = tacos(:veggie)
end
test "should redirect if not logged in" do
get new_taco_url
assert_redirected_to login_path
end
test "should get new if logged in" do
session[:user_id] = users(:pat).id
get new_taco_url
assert_response :success
end
test "should create new taco" do
session[:user_id] = users(:pat).id
assert_difference("Taco.count") do
post tacos_url, params: {taco: {name: "Yummy", price: 2.50}}
end
end
end

How do I check both rendering :new and JSON response?

I try to add RSpec test for invalid entry create controller action, which responds to HTTP POST verb.
it "case of invalid entry" do
program=FactoryGirl.attributes_for(:program, :faculty_id => faculty.id, :code=>"воруй&убивай")
post :create, {:program=>program, :faculty_shortcut=>faculty }
expect(response).to render_template(:new)
end
Controller:
if #program.save
redirect_to faculty_program_path(faculty, #program)
else
render :new,:json => #program.errors
end
What I get from RSpec
Failure/Error: expect(response).to render_template(:new)
expecting <"new"> but rendering with <[]>
This means that the record has been saved and you have been redirected. If you want to check behaviour when invalid data is passed, do not rely on existing validations as they might change in the future. Just stub valid? method:
it "case of invalid entry" do
program=FactoryGirl.attributes_for(:program)
allow_any_instance_of(Program).to receive(:valid?).and_return false
post :create, {:program=>program, :faculty_shortcut=>faculty }
expect(response).to render_template(:new)
end
Stubbing valid? has this advantage that your test will not change when you change your validations. Validations on their own should be tested within model tests, this way single change in the code won't cause multiple test fails.

How do I get at the params included in an RSpec response to examine it?

I am writing a controller spec (RSpec with Devise), and I know that the response returns the parameter I passed in to it (correctly so), because I see it in the output as this:
...#params={"email" => "maddison_stokes#schumm.org", ...
This is my controller spec:
describe 'POST #create' do
context 'when invited user IS an existing user' do
before :each do
#users = [
attributes_for(:user),
attributes_for(:user),
attributes_for(:user)
]
end
it 'correctly finds User record of invited user' do
login_user
post :create, { email: #users.first[:email] }
expect(response.params[:email]).to include(#users.first[:email])
end
end
end
When I run the test above, I get this error:
1) Users::InvitationsController POST #create when invited user IS an existing user correctly finds User record of invited user
Failure/Error: expect(response.params[:email]).to include(#users.first[:email])
NoMethodError:
undefined method `params' for #<ActionController::TestResponse:0x007fa50d1ee6a0>
This is what the entire output of the response looks like:
https://gist.github.com/marcamillion/c2e3f4d0bdae05c2be1f
I tried to paste it here, but SO screamed at me for exceeding the 30,000 character limit. I would have truncated it, but didn't want to remove any info that may be necessary.
What I am trying to do is basically verify that the email in params[:email] contained in the response is the same email I passed to it. I know it is intuitively but I would like to do it programmatically.
undefined method `params' for
ActionController::TestResponse:0x007fa50d1ee6a0
You should use controller.params[:email] instead of response.params[:email]
params are bound to the controller not response.

'No route matches' error while using Factory Girl on Rails

I've been trying to use FactoryGirl for tests on my Rails application, but I'm running into difficulty with it.
I feel as if there must be something fairly obvious I'm doing wrong, but after much searching I haven't been able to figure out the cause.
I'm trying to run a test to confirm the 'show' action is successful on one of my controllers.
Here's the error message I'm getting:
Failure/Error: get 'show'
ActionController::UrlGenerationError:
No route matches {:action=>"show", :controller=>"simple_requests"}
Below are the relevant code snippets leading to this outcome.
/spec/controllers/simple_requests_controller_spec.rb
require 'spec_helper'
describe SimpleRequestsController do
describe "GET 'show'" do
before do
#simple_request = build(:simple_request)
end
it "should be successful" do
get 'show'
expect(response).to be_success
end
end
end
/factories/simple_requests_controller_spec.rb
FactoryGirl.define do
factory :simple_request do
id 123
full_name "Testie McTesterson"
company "Test Company"
role "Analyst"
email "foobar#foobs.com"
phone "000888"
message "Test question?"
end
end
/controllers/simple_requests_controller.rb
def show
authorize SimpleRequest #For pundit
#simple_request = SimpleRequest.find(params[:id])
end
I have two hypotheses as to why this may be happening:
1) Rspec is looking for an id for the 'show' action, but somehow can't find it. (Although there is one in the Factory, and I've yet to figure out how it wouldn't be flowing through.)
2) Pundit is causing issues, since the show action may require authorization (although commenting out the 'authorize' line makes no difference at present)
Any and all thoughts welcome :)
EDIT
Pasting below the output of rake routes | grep simple_requests
simple_requests GET /simple_requests(.:format) simple_requests#index
POST /simple_requests(.:format) simple_requests#create
new_simple_request GET /simple_requests/new(.:format) simple_requests#new
edit_simple_request GET /simple_requests/:id/edit(.:format) simple_requests#edit
simple_request GET /simple_requests/:id(.:format) simple_requests#show
PATCH /simple_requests/:id(.:format) simple_requests#update
PUT /simple_requests/:id(.:format) simple_requests#update
DELETE /simple_requests/:id(.:format) simple_requests#destroy
Edit 2 - Adding ID parameter
I have now also attempted to add an id as follows:
it "should be successful" do
get 'show', id: #simple_request.id
expect(response).to be_success
end
This time I received the following error message
ActiveRecord::RecordNotFound: Couldn't find SimpleRequest with 'id'=123
'123' is the ID in my /factories - I think I must be missing something to get this working, but can't figure out what yet.
Your SimpleRequest does not have an Id. You need to use create instead of build
before do
#simple_request = create(:simple_request)
end
it "should be successful" do
get 'show', id: #simple_request.id
expect(response).to be_success
end
Try this:
before do
#simple_request = create :simple_request
end
it "should be successful" do
get 'show', id: #simple_request.id
expect(response).to be_success
end
It's a show view, so you'll have to supply your request with an ID and you'll have to actually create a record with create instead of build in your before block.

RSpec Test of Custom Devise Session Controller Fails with AbstractController::ActionNotFound

I am currently trying to test a custom Devise session controller with rspec. My controller looks like this:
class SessionsController < Devise::SessionsController
def create
#valid email?
if !(params[:email] =~ /^[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/)
set_flash_message :notice, "Please enter a valid e-mail address!"
end
super
end
end
My RSpec Controller Test is this:
require 'spec_helper'
require 'devise/test_helpers'
describe SessionsController do
it "should put a warning on invalid mail address login attempt" do
post :create, :user => {:email => 'invalidEmailAddress'}
response.should contain "Please enter a valid e-mail address!"
end
it "should put no warning on valid mail address login attempt" do
pending
end
end
If I execute the RSpec Test it fails with the following line:
Failure/Error: post :new, :user => {:email => 'invalidEmailAddress'}
AbstractController::ActionNotFound
# ./spec/controllers/sessions_controller_spec.rb:7
Tips from the plataformatec Devise Wiki as well as this post did not solve this issue. Thanks for your help.
Addition
I investigated further. I was actually able to "remove" the error with the following addition to the controller spec:
before(:each) do
request.env['devise.mapping'] = Devise.mappings[:user]
end
But now a new error appears:
Failure/Error: post :create #currently fails with multiple render warning
Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".
Even with the create method left out in the inheriting controller the error appears. The error does not appear on get :new for example. It seems to be post :create only.
I am out of ideas? Any help?
Thanks!
I finally fixed my problem by doing including the devise test helpers, calling the method setup_controller_for_warden in my test AND doing request.env["devise.mapping"] = Devise.mappings[:user]. Like so:
require 'test_helper'
class SessionsControllerTest < ActionController::TestCase
include Devise::TestHelpers
test "should reject invalid captcha" do
setup_controller_for_warden
request.env["devise.mapping"] = Devise.mappings[:user]
get :new
assert_response :success
end
end
Not sure about your double render problem though, are you sure your supposed to call post :create then render? i'm not sure how rspec is supposed to work.

Resources