I`m trying to test my controller with rspec and always get an error.
users_controller.rb:
def update
#user.update_attributes!(params[:user])
redirect_to #user, :status => 202, :text => render_to_string(:partial => "users/show", :type => "json", :locals => {:user => #user})
#notice, that redirect_to was reinitialized and :text is a parameter for response_body
end
_show.tokamak
user {
id user.id
email user.email
username user.username
}
spec file
it "should NOT update user username" do
username = #user.username
put 'update', :id => #user.id, :user => {:username => username+"abc"}, :format => :json
response.status.should be(202)
response.headers["Location"].length.should be > 0
puts response.body
#user.reload
#user.username.should eq(username)
end
end
So I get an error:
Failure/Error: put 'update', :id =>
#user.id, :user => {:username =>
username+"abc"}, :format => :json
ActionView::Template::Error:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]
# C:/Users/makaroni4/free_frog/ffapi/app/views/users/_show.tokamak:1:in
_app_views_users__show_tokamak___509498818
_32151168_368311673'
# C:/Users/makaroni4/XXX/XXX/app/controllers/users_controller.rb:22:in
update'
# ./users_controller_spec.rb:34:in
`block (4 levels) in '
So may be I call render_to_string method wrong?
Try stubbing out find?
mock_user = User.stub(:find).with(#user.id) {#user}
To be honest I'd go a few steps further and make sure you mock and stub most of the relevant behavior of the User object (or whatever class #user is). Keep in mind you're only testing that the controller action returns what you expect if you give it valid input--not that the model itself does the right thing.
I had a lot of difficulty wrapping my head around the differences in model vs. controller specs...
I hope this helps...if not, I apologize in advance...
EDIT:
I'll take this a step futher and suggest this test is actually a model test. The actual controller test would be something like as the way your spec test should behave:
it "should NOT update user with invalid input" do
mock_user = mock_model(User, {}).as_null_object
User.stub(:find).with("12") {mock_user}
User.stub(:update_attributes).with({}).and_return(false)
put 'update', :id => "12"
# test that your output is correct, or even if the render target is what you expect.
end
Related
I am trying to write a controller spec to test that the right partial is rendering after a post request.
Here is the controller method being posted to:
def lookup
#guest = Guest.where("mobile_number = ?", params[:lookup_mobile_phone_number]).first_or_initialize do |g|
g.mobile_number = params[:lookup_mobile_phone_number]
end
if #guest.new_record?
#visit = Visit.new(hotel_id: params[:hotel_id])
render partial: "guests/form"
else
#visit = Visit.new(guest_id: #guest.id, hotel_id: params[:hotel_id])
render partial: "visits/form"
end
end
Here is the spec/controllers/guests_controller_spec.rb I wrote that is failing:
RSpec.describe GuestsController, :type => :controller do
describe "#lookup" do
render_views
let!(:returning_guest) { create(:test_guest) }
context "when guest is already registered with hotel" do
it "renders visits/form" do
post :lookup, :guest => { :lookup_mobile_phone_number => "5553331212"}
expect(response).to render_template(:partial => 'visits/form')
end
end
end
end
Here is the factory I'm using for :test_guest
FactoryGirl.define do
factory :test_guest, :class => 'Guest' do
name 'Jack Guest'
mobile_number '5553331212'
end
end
This is the response I am getting when the test fails
1) GuestsController#lookup when guest is already registered with hotel renders visits/form
Failure/Error: expect(response).to render_template(:partial => 'visits/form')
expecting partial <visits/form> but action rendered <["shared/_hotel_agent_name", "_hotel_agent_name", "guests/_form", "_form"]>.
Expected {"shared/_hotel_agent_name"=>1, "_hotel_agent_name"=>1, "guests/_form"=>1, "_form"=>1} to include "visits/form".
# ./spec/controllers/guests_controller_spec.rb:16:in `block (4 levels) in <top (required)>'
I've been hacking away at this a for a few days now, trying different approaches found on here with no luck. Any help would be much appreciated :)
You send
post :lookup, :guest => { :lookup_mobile_phone_number => "5553331212"}
but in controller, you use
params[:lookup_mobile_phone_number]
not
params[:guest][:lookup_mobile_phone_number]
So to fix it, according to your controller, do
post :lookup, :lookup_mobile_phone_number => "5553331212"
After a factory girl create I have the following
it "should display the email of user if public" do
#user.update_attributes(:public_email => true)
# I have also tried
# #user.public_email = true
# and
# #user.toggle!(public_email)
#user.save
puts "EMAIL IS #{#user.public_email}"
get :show, :id => #user.id
response.should have_selector("dt", :content => "Email")
end
Rspec will print that "EMAIL IS true" but in the view, public_email is not true (I have <%= "User email is #{#user.public_email}." %> and that prints false).
All my other tests work as expected and it works fine in development.
Why is this happening?
EDIT:
This is my own fault. I was trying to avoid writing the line get :show, :id => #user.id a bunch of times, so I had another before(:each) do call after those tests, and I thought by listing it after them it would not count, but apparently it does.
I have the following controller RSpec test that should work however when I try to execute the below code:
require 'spec_helper'
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe UsersController do
include Devise::TestHelpers #Include devise test helpers
render_views # Render devise views
describe "GET 'show'"
before(:each) do
#user = User.find(params[:id])
#attr = {:initials => 'EU', :name => 'Example', :full_name => 'Example User',
:email => 'user#example.com', :password => 'password', :password_confirmation => 'password'
##attr = User.all
}
end
it 'should be successful when showing OWN details' do
get :show, :id => #attr
response.should be_success
end
it 'should find the correct user' do
get :show, :id => #attr
assigns(#attr).should == #attr
end
end
However I am getting the following output: Undefined locacl variable or method 'params' for RSPRC I believe this set up should be correct.
I see a few things going on here.
Your calling params within the before statement but where is the id ever set? No where in this code do I see where the user id is ever actually determined/found. You should set #user explicitly using either an id that you know exists or doing something such as User.first.
Then instead of calling
get :show, :id => #attr
you should be calling
get :show, :id => #user.id
Also I'm not sure why you need to include spec_helper twice. Line 2 should be able to be removed.
One more thing - assigns(#attr).should == #attr doesn't make any sense. It should be assigns(:attr).should == #attr. Otherwise you would be passing the value of #attr into the assigns method.
I need to know how to test this controller action
def create_mobile
if mobile_user = MobileUser.authenticate(params[:email], params[:password])
session[:mobile_user_id] = mobile_user.id
respond_to do |format|
format.json { head :ok }
end
else
respond_to do |format|
format.json { head :unauthorised }
end
end
end
The route is a post request to sessions/create_mobile and as you can see the action only responds to json
My current controller spec looks like this
describe SessionsController, "Logging in" do
before(:each) do
#mobile_user = FactoryGirl.create(:valid_mobile_user)
end
it "Should log in mobile user" do
#request.env["HTTP_ACCEPT"] = "application/json"
post :create_mobile, {:password => #mobile_user.password, :email => #mobile_user.email}
response.should be_success
end
it "should fail log in mobile user" do
#request.env["HTTP_ACCEPT"] = "application/json"
post :create_mobile, {:password => 'joe', :email => #mobile_user.email}
response.should_not be_success
end
end
The test results in
1) SessionsController Logging in should log in mobile user
Failure/Error: response.should be_success
expected success? to return true, got false
# ./spec/controllers/sessions_controller_spec.rb:11:in `block (2 levels) in <top (required)>'
So there is either a problem with my application code or there is a problem with my test code but either way there is no problem with the authenticate method as the MobileUser model spec passes which looks like this
it "should authenticate" do
mu = FactoryGirl.create(:valid_mobile_user)
assert_equal 1, MobileUser.count
assert_equal mu, MobileUser.authenticate(mu.email, mu.password)
end
Any help in sorting this out greatly appreciated.
Thanks in advance
UPDATE
As suggested below, using
post :create_mobile, {:password => 'joe', :email => #mobile_user.email} :format => :json
or using
#request.env["HTTP_ACCEPT"] = "application/json"
or a combination of both makes no difference
UPDATE 2
The test has just started working for no reason that I can fathom (other than I never understood why it wasn't working in the first place).
Totally strange!
I met the same issue recently. below is my test code
post :create, :login => 'mock_user', :password => 'passwd', :format => :json
expected = {
:login => 'mock_user'
}.to_json
session[:user_id].should == mock_user.id
response.body.should == expected
The test result is false, caused by response.body is blank. I checked the test.log, and found got 406 unacceptable request error in controller. After googled a lot, I changed to :format => 'json'. Then I got the expected test result.
Since you're trying to post JSON to the controller, shouldn't you convert your hash to JSON?
post :create_mobile, {:password => 'joe', :email => #mobile_user.email}.to_json
If you don't want to add seven characters to your code just to see if it works, then you should output your params to logger to see what they look like.
def create_mobile
logger.info "LOGIN PARAMS U/N: #{params[:email]}, P/W: #{params[:password]}"
...
Then tail -f log/test.log to see what your controller looks like during your test.
have you tried adding a :format option to the post statement
I'm writing tests for my controller. They are very simple, but this error has kept popping up. This is my controller
def show
id=params[:id]
#user=User.find(:first,id)
end
My test
before(:each) do
#user = Fabricate(:user)
sign_in #user
end
...
it "should be successful" do
get "show", :id => #user
response.should be_success
end
And the error message
1) UsersController GET 'show' for the logged in user should be successful
Failure/Error: get "show", :id => #user
TypeError:
can't convert Symbol into Integer
# ./app/controllers/users_controller.rb:6:in `show'
# ./spec/controllers/users_controller_spec.rb:31:in `block (4 levels) in <top (required)>'
your controller is where the mistake is. The find method automatically only returns the first result (it is equivalent in code to User.where(:id => params[:id]).first). Try removing the :first symbol and simply pass in id (User.find(id))
get "show", :id => #user
Your problem here is likely with #user, whose value in the context of your spec is not clear from the example you've posted. You should be passing an integer record id as the value for the params argument to get, for example :id => 1.