Having trouble testing variable values from a controller using RSpec.
Relevant controller code:
class ToysController < ApplicationController
def claim
toy = Toy.find(params[:toy_id])
current_user.toys << toy
toy.status = "claimed"
render :index
end
end
This definitely works -- I know because I puts toy.inspect after it happens, and it's fine. But I can't test it. Here's what my current test looks like, after a lot of messy attempts:
require 'spec_helper'
describe ToysController do
describe "GET 'claim'" do
let(:james) {create(:user)}
let(:toy) {create(:toy)}
before do
OmniAuth.config.mock_auth[:google] = {
uid: james.uid
}
session[:user_id] = james.id
end
it "can be claimed by a user" do
get :claim, toy_id: toy.id
assigns(:toy).user.should eq james.id
end
end
end
When I run the test, I get all sorts of errors on assigns(:toy).user.should indicating that toy is Nil. I've tried messing with the assigns syntax in lots of ways, because I was unable to find the docs for it.
What am I doing wrong? What's the right way to see what the controller does with the user and the toy passed to it?
Edit: Trying to phase over to instance variables, but it still doesn't do the trick. Here's my code again with instance variables (different var names, same results):
Ideas controller:
def claim
#idea = Idea.find(params[:idea_id])
current_user.ideas << #idea
#idea.status = "claimed"
render :index
end
Test:
describe "GET 'claim'" do
let(:james) {create(:user)}
let(:si_title) {create(:idea)}
before do
OmniAuth.config.mock_auth[:google] = {
uid: james.uid
}
session[:user_id] = james.id
end
it "can be claimed by a user" do
get :claim, idea_id: si_title.id
puts assigns(si_title).inspect
end
end
Output: nil.
Solved! Test now reads like this:
describe "GET #claim" do
let(:james) {create(:user)}
let(:si_title) {create(:idea)}
before do
OmniAuth.config.mock_auth[:google] = {
uid: james.uid
}
session[:user_id] = james.id
end
it "can be claimed by a user" do
get :claim, idea_id: si_title.id
assigns(:idea).user_id.should eq james.id
end
end
My mistakes:
Not using a colon to prefix the instance variable name in the assigns.
Using the incorrect variable name in the assigns.
Try replacing toy variable in Controller with #toy. assigns has access only to instance variables.
Related
Hi I am new to rspec (and unit testing in general) and want to test the following method:
class HelloController < ApplicationController
def hello_world
user = User.find(4)
#subscription = 10.00
render :text => "Done."
end
end
I am trying to use Rspec like so:
Describe HelloController, :type => :controller do
describe "get hello_world" do
it "should render the text 'done'" do
get :hello_world
expect(response.body).to include_text("Done.")
end
end
end
I would like to simply test that the method works properly and renders the test "done". I get the following error when I run the test:
Failure/Error: user = User.find(4)
ActiveRecord::RecordNotFound:
Couldn't find User with 'id'=4
But how do I properly create a user with that id before executing it? I have tried the following based on other tutorials and questions but it doesn't work:
describe "get hello_world" do
let(:user) {User.create(id: 4)}
it "should render the text 'done'" do
get :hello_world
expect(response.body).to include_text("Done.")
end
end
Thank you in advance.
Hey so really no action (e.g. def hello_world) should rely on a specific id. So a simple alternative could be to use user = User.last or to find the user by name user = User.find_by(name: "name"). Then in the test you would create any user if you using User.last in the action.
describe "get hello_world" do
let(:user) {User.create!}
it "should render the text 'done'" do
get :hello_world
expect(response.body).to include_text("Done.")
end
end
or if you are searching by name you can make a user with that name;
describe "get hello_world" do
let(:user) {User.create!(name: "name")}
it "should render the text 'done'" do
get :hello_world
expect(response.body).to include_text("Done.")
end
end
Hope this helps, questions welcome.
Do you really mean to use 'user = User.find(4)'? If you really meant to do that, you should stub the User's find method and return a user object.
it "should render the text 'done'" do
u = User.new #a new user, your test database is empty, so there's no user with id 4
User.stub(find: u) #stub the User's find method to return that new user
get :hello_world
expect(response.body).to include_text("Done.")
end
Another option is to send the user_id via params
it "should render the text 'done'" do
u = User.create(.... your user params)
get :hello_world, user_id: u.id
expect(response.body).to include_text("Done.")
end
and
def hello_world
user = User.find(params[:user_id])
#subscription = 10.00
render :text => "Done."
end
Anyway, I don't think you should be doing that, a hardcoded id is a bad sign. If you need to control users registrations and logins you can use something like Devise, and you may need to create an login a user before the spec.
i am using rails and want to write a test for password reset in Rspec. i am quite new to testing.
this is what i have done so far:
require 'rails_helper'
describe UsersController, type: :controller do
describe 'post #reset_password' do
let(:user) { create(:user) }
context "reset password" do
def do_request
patch :update_password
end
before { do_request }
it { expect(ActionMailer::Base.deliveries.count(1) }
end
end
end
every time i run this it gives ma an syntax error in
"it { expect(ActionMailer::Base.deliveries.count(1) } ".
i want to check whether the email successfully sent of not and if the user have key in the email.
Thanks!
1) you miss ) at last here so got syntax error
it { expect(ActionMailer::Base.deliveries.count(1) }
to
it { expect(ActionMailer::Base.deliveries.count(1)) }
2)
If you want to check total deliveries. you can try
it 'should send an email' do
ActionMailer::Base.deliveries.count.should == 1
end
also check sender
it 'renders the sender email' do
ActionMailer::Base.deliveries.first.from.should == ['notifications#domain.com']
end
Also check subject line
it 'should set the subject to the correct subject' do
ActionMailer::Base.deliveries.first.subject.should == 'Here Is Your Story!'
end
The problems you're having will most likely be fixed by writing better tests.
Here's generally how you would write tests for something like this.
Lets suppose in your routes file you have a post route that looks something like this
# config/routes.rb
post "/user/:id/reset_password", to: "users#reset_password"
And your User controller looks something like this
# app/controllers/users_controller.rb
class UsersController
...
def reset_password
user = User.find(params[:id])
user.reset_password!
SomeMailClass.email_reset_instructions(user)
end
end
and your User.rb model looks something like this
# app/models/user.rb
class User < ActiveRecord::Base
def reset_password!
update!(password: nil) # or whatever way you want/need to reset the password
end
end
and you have some type of mailing class to send your email
# app/models/some_mail_class.rb
class SomeMailClass
def self.email_reset_instructions(user)
# do something to send email...
end
end
The way you would go about testing this in the controller would be
# spec/controllers/users_controller_spec.rb
require 'rails_helper'
describe UsersController, type: :controller do
it "#reset_password" do
user_id = double(:user_id)
user = double(:user)
expect(User).to receive(:find).with(user_id).and_return(user)
expect(user).to receive(:reset_password!).and_return(true)
expect(SomeMailClass).to receive(:email_reset_instructions).with(user)
post :reset_password, id: user_id
end
end
But you shouldn't stop there. Because the implementation of the newly made method reset_password! and the SomeMailClass has yet to be tested. So you would write model/unit tests like this for them
# spec/models/user_spec.rb
require "rails_helper"
describe User do
it ".reset_password!" do
user = User.create(password: "foo")
expect(user.password).to eq "foo"
user.reset_password!
expect(user.password).to eq nil
end
end
Then you might install vcr and factory_girl gems and use them like so to test your mailer
# spec/models/some_mail_class_spec.rb
require "rails_helper"
describe SomeMailClass do
VCR.use_cassette "email_reset_instructions" do |cassette|
it ".email_reset_instructions" do
user = FactoryGirl.create(:user)
SomeMailClass.email_reset_instructions(user)
# you can write some expectations on the cassette obj to test.
# or you can write whatever expectations you need/desire
end
end
end
And in the end if there was something happening on the front end that a user would click that made this post request you would write a feature test for it as well.
Hope this helps!
I am just starting with RSpec and I am a bit lost. Can anyone please tell me how to write controller test for this method?
def list_c
#c = params.has_key?(:car) ? Car.new(car_listing_params) : Car.new()
#car.user = #user
#car.number_of_beds = 4 unless #car.number_of_beds
#car.car_type_tag = 'car' unless #car.car_type
#page_title = t('home.list_your_car')
end
A small example of controller test with rspec. You may get some idea from here.
# define your helper here.
require 'rails_helper'
# respect method to define test for your controller.
RSpec.describe YourController, type: :controller do
#define your factories for the set of your test
# you will decide which one you need to create from factory. Factory girl
# is used to mock factories.
# #c = params.has_key?(:car) ? Car.new(car_listing_params) : Car.new()
# #car.user = #user
# #car.number_of_beds = 4 unless #car.number_of_beds
# #car.car_type_tag = 'car' unless #car.car_type
# #page_title = t('home.list_your_car')
let(:user) { FactoryGirl.create :user }
before { sign_in user }
describe '#title' do
context 'with valid params' do
before do
# in before block, you can request a http call
# with parameter. This action repeats before each test runs.
get :your_action_of_current_controller, {a_param: a_value, a_date: "16-4-2015"}
end
it "renders successfully" do
# when you call a get request it assigns a response object.
expect(response).to be_success
end
it "returns json" do
expect(response.content_type).to be == 'application/json'
end
end
end
end
This following Controller test is failing, and I can't figure out why:
describe "GET 'index'" do
before(:each) do
#outings = FactoryGirl.create_list(:outing, 30)
#user = FactoryGirl.create(:user)
end
it "should be successful" do
get :index
response.should be_success
end
end
Rspec offers up the (rather unhelpful) error:
Failure/Error: response.should be_success
expected success? to return true, got false
Here's the code for the actual Controller, too:
def index
if #user
#outings = Outing.where(:user_id => #user.id)
#outing_invites = OutingGuest.where(:user_id => #user.id)
else
flash[:warning] = "You must log in to view your Outings!"
redirect_to root_path
end
end
Anyone have an idea what's causing my test to fail? I assume it may have something to do with the conditional in the Outing Controller, but I have no idea what a passing test would look like...
You're confusing instance variables between two separate classes - the controller is its own class and the specification is its own class. They don't share state. You could try this simple example to get a better understanding...
def index
// obvious bad code, but used to prove a point
#user = User.first
if #user
#outings = Outing.where(:user_id => #user.id)
#outing_invites = OutingGuest.where(:user_id => #user.id)
else
flash[:warning] = "You must log in to view your Outings!"
redirect_to root_path
end
end
I'll guess that FactoryGirl.create_list(:outing, 30) doesn't create an outing associating the first user with the outing since you're creating the user after you create the outing so your Outing.where will fail as well.
Its important to understand that when you are including the database in your test stack the database needs to contain the data in the way the test expects. So if your controller is querying for outings belonging to a specific user your spec needs to setup the environment such that the user the controller will retrieve (in this case, the terrible line with User.first from my example) will also have the outings associated with it that the specification is expecting.
Using Rspec with Factory Girl. Trying to check out what data is being assigned in my controller (and test against it). Every post I've read says I should be able to get something out of assigns() but it keeps returning nill
Controller
def index
#stickies = Sticky.where(:user_id => current_user.id)
end
Spec
it "should assign stickies" do
foo = assigns(:stickies)
puts "foo = #{foo}"
end
Output
foo =
Am I using the wrong syntax? Is there a better way to do this? Thanks!!
You have to invoke the action first
describe StickiesController do
describe "GET index" do
it "should assign stickies" do
get :index
assigns(:stickies).should_not be_nil
end
end
end
If you are using the rspec > 2.99 you can use:
expect(assigns(:stickies)).not_to be_nil