Issue with Failures in Chapter 7 of Hartl Tutorial - ruby

When I run bundle exec rspec spec/ I'm getting 21 examples and 3 failures. Those failures being:
Failures:
1) User has_password? method should be true if the passwords match
Failure/Error: #user.has_password?(#attr[:password]).should be_true
NoMethodError:
undefined method has_password?' for nil:NilClass
# ./spec/models/user_spec.rb:47:inblock (3 levels) in '
2) User has_password? method should be false if the passwords don't match
Failure/Error: #user.has_password?("invalid").should be_false
NoMethodError:
undefined method has_password?' for nil:NilClass
# ./spec/models/user_spec.rb:51:inblock (3 levels) in '
3) User password validations should accept valid email addresses
Failure/Error: it "should reject invalid email addresses" do
NoMethodError:
undefined method it' for #<RSpec::Core::ExampleGroup::Nested_3::Nested_3:0x00000102eb38b0>
# ./spec/models/user_spec.rb:97:inblock (3 levels) in '
I'll post my user_spec.rb file bc I think it's almost right, but not completely. Note the commented out ends, I had those in play before but thought they were wrong so commented them out.
require 'spec_helper'
describe User do
before(:each) do
#attr = {
:name => "Example User",
:email => "user#example.com",
:password => "foobar",
:password_confirmation => "foobar" }
end
it "should create a new instance given valid attributes" do
User.create!(#attr)
end
describe "password encryption" do
before(:each) do
#user = User.create!(#attr)
end
it "should have an encrypted password attribute" do
#user.should respond_to(:encrypted_password)
end
it "should set the encrypted password" do
#user.encrypted_password.should_not be_blank
end
end
describe "has_password? method" do
it "should be true if the passwords match" do
#user.has_password?(#attr[:password]).should be_true
end
it "should be false if the passwords don't match" do
#user.has_password?("invalid").should be_false
end
end
describe "password validations" do
it "should require a password" do
User.new(#attr.merge(:password => "", :password_confirmation => "")).
should_not be_valid
end
it "should require a matching password confirmation" do
User.new(#attr.merge(:password_confirmation => "invalid")).
should_not be_valid
end
it "should reject short passwords" do
short = "a" * 5
hash = #attr.merge(:password => short, :password_confirmation => short)
User.new(hash).should_not be_valid
end
it "should reject long passwords" do
short = "a" * 5
hash = #attr.merge(:password => short, :password_confirmation => short)
User.new(hash).should_not be_valid
end
it "should require a name" do
no_name_user = User.new(#attr.merge(:name => ""))
no_name_user.should_not be_valid
end
it "should require an email address" do
no_email_user = User.new(#attr.merge(:email => ""))
no_email_user.should_not be_valid
end
it "should accept valid email addresses" do
addresses = %w[user#foo.com THE_USER#foo.bar.org first.last#foo.jp]
addresses.each do |address|
valid_email_user = User.new(#attr.merge(:email => address))
valid_email_user.should be_valid
end
#end
it "should reject invalid email addresses" do
addresses = %w[user#foo,com user_at_foo.org example.user#foo.]
addresses.each do |address|
invalid_email_user = User.new(#attr.merge(:email => address))
invalid_email_user.should_not be_valid
end
#end
it "should reject duplicate email addresses" do
# Put a user with given email address into the database.
User.create!(#attr)
user_with_duplicate_email = User.new(#attr)
user_with_duplicate_email.should_not be_valid
end
it "should reject email addresses identical up to case" do
upcased_email = #attr[:email].upcase
User.create!(#attr.merge(:email => upcased_email))
user_with_duplicate_email = User.new(#attr)
user_with_duplicate_email.should_not be_valid
end
it "should reject names that are too long" do
long_name = "a" * 51
long_name_user = User.new(#attr.merge(:name => long_name))
long_name_user.should_not be_valid
end
end
end
end
end
My user.rb file is fine I think.
So the 3 failures thing is one aspect of my problem, but the thing that really worries me is the following command:
bundle exec rspec spec/models/user_spec.rb -e "has_password\? method"
The result in terminal is this:
No examples matched {:full_description=>/(?-mix:has_password\\?\ method)/}.
Finished in 0.00003 seconds
0 examples, 0 failures
According to Hartl I should have 2 examples, and 0 failures. Ideas? Any input appreciated :)

in your user_spec.rb file.
make sure you have:
before(:each) do
#user = User.create!(#attr)
end
right after the following line:
describe "has_password? method" do
it's missing from the code in the tutorial. you'll see it's part of the password encryption block. it looks like it stubs out a user for that test. it's not very DRY...probably a way to have that stub block run for each describe block, but that's a bit further along than i am. :) hope it helps...got my tests working.

Related

When Running Rspec and Sinatra, I keep getting ArgumentError: wrong number of arguments (given 2, expected 0)

I've got a class method called authenticate, which works on the User class.
def self.authenticate(email:, password:)
result = DatabaseConnection.query("SELECT * FROM users WHERE email = '#{email}'")
User.new(result[0]['id'], result[0]['email'])
end
I have an Rspec test;
feature 'authentication' do
it 'a user can sign in' do
User.create(email: 'test#example.com', password: 'password123')
visit 'sessions/new'
fill_in(:email, with: 'test#example.com')
fill_in(:password, with: 'password123')
click_button 'Sign In'
expect(page).to have_content 'Welcome, test#example.com'
end
end
When running Rspec, I get the following error;
1) authentication a user can sign in
Failure/Error:
def initialize(id:, email:)
#id = id
#email = email
end
ArgumentError:
wrong number of arguments (given 2, expected 0)
# ./lib/user.rb:15:in `initialize'
# ./lib/user.rb:23:in `new'
# ./lib/user.rb:23:in `authenticate'
# ./app.rb:84:in `block in <class:BookmarkManager>'
Below is my Sinatra app;
require 'sinatra/base'
require './lib/bookmark'
require './lib/user'
require './database_connection_setup.rb'
require 'uri'
require 'sinatra/flash'
require_relative './lib/tag'
require_relative './lib/bookmark_tag'
class BookmarkManager < Sinatra::Base
enable :sessions, :method_override
register Sinatra::Flash
get '/' do
"Bookmark Manager"
end
get '/bookmarks' do
#user = User.find(session[:user_id])
#bookmarks = Bookmark.all
erb :'bookmarks/index'
end
post '/bookmarks' do
flash[:notice] = "You must submit a valid URL" unless Bookmark.create(url: params[:url], title: params[:title])
redirect '/bookmarks'
end
get '/bookmarks/new' do
erb :'bookmarks/new'
end
delete '/bookmarks/:id' do
Bookmark.delete(id: params[:id])
redirect '/bookmarks'
end
patch '/bookmarks/:id' do
Bookmark.update(id: params[:id], title: params[:title], url: params[:url])
redirect('/bookmarks')
end
get '/bookmarks/:id/edit' do
#bookmark = Bookmark.find(id: params[:id])
erb :'bookmarks/edit'
end
get '/bookmarks/:id/comments/new' do
#bookmark_id = params[:id]
erb :'comments/new'
end
post '/bookmarks/:id/comments' do
Comment.create(text: params[:comment], bookmark_id: params[:id])
redirect '/bookmarks'
end
get '/bookmarks/:id/tags/new' do
#bookmark_id = params[:id]
erb :'/tags/new'
end
post '/bookmarks:id/tags' do
tag = Tag.create(content: params[:tag])
BookmarkTag.create(bookmark_id: params[:id], tag_id: tag.id)
redirect '/bookmarks'
end
get '/users/new' do
erb :'users/new'
end
post '/users' do
user = User.create(email: params[:email], password: params[:password])
session[:user_id] = user.id
redirect '/bookmarks'
end
get '/sessions/new' do
erb :'sessions/new'
end
post '/sessions' do
user = User.authenticate(email: params[:email], password: params[:password])
if user
session[:user_id] = user.id
redirect('/bookmarks')
else
flash[:notice] = 'Please check your email or password.'
redirect('/sessions/new')
end
end
run! if app_file == $0
end
Below is the full User class
require_relative './database_connection'
require 'bcrypt'
class User
def self.create(email:, password:)
encypted_password = BCrypt::Password.create(password
)
result = DatabaseConnection.query("INSERT INTO users (email, password) VALUES('#{email}', '#{encypted_password}') RETURNING id, email;")
User.new(id: result[0]['id'], email: result[0]['email'])
end
attr_reader :id, :email
def initialize(id:, email:)
#id = id
#email = email
end
def self.authenticate(email:, password:)
result = DatabaseConnection.query("SELECT * FROM users WHERE email = '#{email}'")
User.new(result[0]['id'], result[0]['email'])
end
def self.find(id)
return nil unless id
result = DatabaseConnection.query("SELECT * FROM users WHERE id = #{id}")
User.new(
id: result[0]['id'],
email: result[0]['email'])
end
end
What I don't understand is, why is Rspec saying it was expecting 0 arguments, when the initialize method clearly requires two arguments (id, and, email)?
I need to take the id and email method from authenticate and deliver it to initialize.
I thought that's what I was doing, but both Rspec and sinatra are saying otherwise.
Thanks, in advance.
Here you are passing id as sequential args (in the authenticate method).
User.new(result[0]['id'], result[0]['email'])
However your User.new expects keyword args:
def initialize(id:, email:)
Simply pass them this way:
User.new(id: result[0]['id'], email: result[0]['email'])
Also, just something I noticed, if your DatabaseConnection.query returns no results your authenticate will raise an error from result[0]['id'] (it will say "Undefined method [] for Nil:NilClass". Maybe you should fix this and add a test case for it, for example:
def self.authenticate(email:, password:)
result = DatabaseConnection.query(
"SELECT * FROM users WHERE email = '#{email}'"
)
record = result[0]
if record
User.new(id: result[0]['id'], email: result[0]['email'])
end
end
This way the method will return nil if there's no matching user, and your if user inside post '/sessions' will work properly.

Test failure gives incomplete error message when using short syntax

There are 2 syntaxes (afaik) for writing tests with RSpec:
The classic/old way:
describe "when user_id is not present" do
before { #micropost.user_id = nil }
it "should not be valid" do
#micropost.should_not be_valid
end
end
That gives this error when failing:
rspec ./spec/models/micropost_spec.rb:19 # Micropost when user_id is
not present should not be valid
And the short syntax:
describe "when user_id is not present" do
before { #micropost.user_id = nil }
it { should_not be_valid }
end
That gives this error when failing:
rspec ./spec/models/micropost_spec.rb:18 # Micropost when user_id is
not present
The last one is missing the "should not be valid" part.
Is there any way to have the complete test failure message, maybe a flag I don't know about?
Note: A more complete error message is still there no matter what:
1) Micropost when user_id is not present
Failure/Error: it { should_not be_valid }
expected #<Micropost id: nil, content: "Lorem ipsum", user_id: nil, created_at: nil, updated_at: nil> not to be valid
# ./spec/models/micropost_spec.rb:18:in `block (3 levels) in <top (required)>'
But the recap at the end is incomplete.
The text you see in
rspec ./spec/models/micropost_spec.rb:19 # Micropost1 when user_id is not present2 should not be valid3
and
rspec ./spec/models/micropost_spec.rb:18 # Micropost1 when user_id is not present2 3
is not the error message, but the test name, which is a concatenation of the current test's description with all its parents' descriptions:
describe Micropost do # <= 1
describe "when user_id is not present" do # <= 2
before { #micropost.user_id = nil }
it "should not be valid" do # <= 3
#micropost.should_not be_valid
end
end
end
In the short (one-liner) syntax - the test itself does not have a description
describe Micropost do # <= 1
describe "when user_id is not present" do # <= 2
before { #micropost.user_id = nil }
it { should_not be_valid } # <= 3 - not described!
end
end
If you want to force a description on this test you could write it like this:
it('should not be valid') { should_not be_valid }
But this kind of beats the purpose of the one-liner, which should be self-explanatory, don't you think?

Why is rspec saying this method is not received with should_receive?

My question is, why do I receive the following rspec error message? (code below) I've stubbed the :update_payment method on the StripeSubscription model. I've been at this for a couple of hours and am perplexed.
Failure/Error: #stripe_msub.should_receive(:update_payment).and_return(#stripe_msub)
(#<StripeSubscription:0xb879154>).update_payment(any args)
expected: 1 time
received: 0 times
###Rspec test###
describe "PUT 'update'" do
context "signed-in teacher" do
before(:each) do
#teacher = Factory(:teacher)
#teacher_upload = Factory(:teacher_upload,:teacher_id=>#teacher.id)
#stripe_mplan = Factory(:stripe_plan)
#new_stripe_card_token = 528
#stripe_msub = Factory(:stripe_subscription,:teacher_id=>#teacher.id,:stripe_plan_id=>#stripe_mplan.id, :email=>#teacher.email,:account_status=>Acemt::Application::STRIPE_SUBSCRIPTION_ACCOUNT_STATUS[:active])
#stripe_msub.stub!(:update_payment).and_return(#stripe_msub)
StripeSubscription.stub!(:update_payment).and_return(#stripe_msub)
StripeSubscription.stub!(:update_attributes).and_return(true)
#stripe_customer = mock('Stripe::Customer')
Stripe::Customer.stub!(:retrieve).with(#stripe_msub.stripe_customer_token).and_return(#stripe_customer)
#stripe_customer.stub(:card=).and_return(true)
#stripe_customer.stub(:save).and_return(true)
test_sign_in(#teacher)
end
it "should update credit card information" do
#stripe_msub.should_receive(:update_payment)
Stripe::Customer.should_receive(:retrieve).with(#stripe_msub.stripe_customer_token).and_return(#stripe_customer)
#stripe_customer.should_receive(:card=)
#stripe_customer.should_receive(:save).and_return(#stripe_customer)
put :update, :teacher_id=>#teacher.id, :stripe_subscription=>{:stripe_plan_id=>#stripe_msub.stripe_plan_id, :teacher_id=>#stripe_msub.teacher_id, :email=>#stripe_msub.email, :stripe_customer_token=>#stripe_msub.stripe_customer_token,:stripe_card_token=>#new_stripe_card_token,:account_status=>#stripe_msub.account_status}
#teacher.stripe_subscription.should == #stripe_msub
response.should redirect_to teacher_path(#teacher)
end
end #signed in teacher
end #PUT update
###controller###
class StripeSubscriptionsController < ApplicationController
before_filter :signed_in_teacher
before_filter :correct_teacher
:
def update
##stripe_subscription = StripeSubscription.find_by_id(params[:id])
#stripe_subscription = #teacher.stripe_subscription
if #stripe_subscription.update_payment(params[:stripe_subscription])
#handle successful update
flash[:success] = "Credit card updated"
sign_in #teacher
redirect_to #teacher
else
render 'edit'
end
end
:
end
###model###
class StripeSubscription < ActiveRecord::Base
#attr_accessible :email, :plan_id, :stripe_customer_token, :teacher_id, :account_status
validates_presence_of :stripe_plan_id
validates_presence_of :email
validates_presence_of :teacher_id
belongs_to :stripe_plan, :class_name=>"StripePlan"
belongs_to :teacher
attr_accessor :stripe_card_token
def save_with_payment
if valid?
customer = Stripe::Customer.create(description: email, plan: stripe_plan_id, card: stripe_card_token)
self.stripe_customer_token = customer.id
save!
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating customer: #{e.message}"
errors.add :base, "There was a problem with your credit card."
end
def update_payment(stripe_params)
if valid?
customer = Stripe::Customer.retrieve(self.stripe_customer_token)
customer.card = stripe_params[:stripe_card_token]
status = customer.save #update card info on Stripe
update_attributes(stripe_params) #save StripeSubscription object
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while updating your credit card: #{e.message}"
errors.add :base, "There was a problem with your credit card."
end
end
You are setting the expectation on an object in your spec (#stripe_msub). In the controller however, you probably load a teacher from the database and get its subscription (#stripe_subscription).
Due to the roundtrip through the database, this object is not the same one as the one you set your expectation on, therefore the one you set your expection on (#stripe_msub in the spec) never receives the method call and therefore rspec complains.
To fix this, you would have to stub away all database calls and make sure, that your object from the spec appears in the controller. I don't know where exactly #teacher is set in the controller (I guess in one of the filters) so I can't give you the exact solution, but it will be something like this:
# I assume this is the implentation of your filter in the controller
def signed_in_teacher
#teacher = Teacher.find_by_id(params[:teacher_id])
end
# Then you would have to add the following mocks/stubs to your spec
Teacher.should_receive(:find_by_id).once.with(#teacher.id).and_return(#teacher)

How can I access Sorcery in my RSpec tests?

Sorcery authentication gem: https://github.com/NoamB/sorcery
Sorcery's creator provides an example Rails app with Sorcery test helpers included in its Test::Unit functional tests: https://github.com/NoamB/sorcery-example-app/blob/master/test/functional/users_controller_test.rb
# Test::Unit functional test example
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
setup do
#user = users(:noam)
end
test "should show user" do
login_user
get :show, :id => #user.to_param
assert_response :success
end
But I can't figure out how to get login_user to work in my RSpec controller specs.
/gems/sorcery-0.7.5/lib/sorcery/test_helpers/rails.rb:7:in `login_user':
undefined method `auto_login' for nil:NilClass (NoMethodError)
Here's the relevant code in the Sorcery gem regarding the above error:
https://github.com/NoamB/sorcery/blob/master/lib/sorcery/test_helpers/rails.rb
module Sorcery
module TestHelpers
module Rails
# logins a user and calls all callbacks
def login_user(user = nil)
user ||= #user
#controller.send(:auto_login,user)
#controller.send(:after_login!,user,[user.send(user.sorcery_config.username_attribute_names.first),'secret'])
end
def logout_user
#controller.send(:logout)
end
end
end
end
UPDATE:
As per Sorcery's documentation "Testing in Rails 3", I have indeed added include Sorcery::TestHelpers::Rails to my spec_helper.rb.
The Sorcery test helper login_user acts on #controller, but I'm getting the error because #controller is nil in my controller spec. Here's my spec:
#spec/controllers/forums_controller_spec.rb
require 'spec_helper'
describe ForumsController do
render_views
describe 'GET new' do
describe 'when guest' do
it 'should deny and redirect' do
get :new
response.should redirect_to(root_path)
end
end
describe 'when admin' do
p #controller #=> nil
#user = User.create!(username: "Test", password: "secret", email: "test#test.com")
login_user # <--------------- where the error occurs
it 'should resolve' do
get :new
response.should render_template(:new)
end
end
end
end
FWIW, I spent a lot of time looking for an answer to this problem. I am using Capybara and RSpec. As it turns out, you need to login manually to using Sorcery to get the login to work.
I've created a Gist on creating integration tests with Sorcery/Rspec/Capybara here:
https://gist.github.com/2359120/9989c14af19a48ba726240d030c414b882b96a8a
You need to include the Sorcery test helpers in your spec_helper
include Sorcery::TestHelpers::Rails
See the sorcery wiki : https://github.com/NoamB/sorcery/wiki/Testing-rails-3
In the example rails app, this is done at https://github.com/NoamB/sorcery-example-app/blob/master/test/test_helper.rb#L13
Updated
Do you have any other Controller specs in the same folder which pass successfully ?
RSpec usually mixes in the required stuff for controller testing for the specs in "spec/controllers" folder.
You could try explicitly marking this as a controller spec by writing
describe ForumsController, :type => :controller do
You need to put your user creation and login into a before(:each) block as follows:
describe 'when admin' do
before(:each) do
#user = User.create!(username: "Test", password: "secret", email: "test#test.com")
login_user
end
it 'should resolve' do
get :new
response.should render_template(:new)
end
end
I've just experienced this dilemma myself and drawing from the input from danneu, diwalak and Birdlevitator (in this thead: rail3/rspec/devise: rspec controller test fails unless I add a dummy=subject.current_user.inspect) I think I can see a solution.
I've been working with a standard rails 3 rspec generated resource from the 'rails generate scaffold' command. Here's the controller rspec file after I modified it to work with a sorcery login:
require 'spec_helper'
# This spec was generated by rspec-rails when you ran the scaffold generator.
# It demonstrates how one might use RSpec to specify the controller code that
# was generated by Rails when you ran the scaffold generator.
#
# It assumes that the implementation code is generated by the rails scaffold
# generator. If you are using any extension libraries to generate different
# controller code, this generated spec may or may not pass.
#
# It only uses APIs available in rails and/or rspec-rails. There are a number
# of tools you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
#
# Compared to earlier versions of this generator, there is very limited use of
# stubs and message expectations in this spec. Stubs are only used when there
# is no simpler way to get a handle on the object needed for the example.
# Message expectations are only used when there is no simpler way to specify
# that an instance is receiving a specific message.
describe RecordsController do
before(:each) do
#user = User.create!(forename: "Billy", surname: "Bob", username: "Test", password: "secret!1", email: "test#test.com")
login_user
end
# This should return the minimal set of attributes required to create a valid
# Record. As you add validations to Record, be sure to
# update the return value of this method accordingly.
def valid_attributes
{ :owner => 'Mr Blobby', :catagory => 'Index'}
end
# This should return the minimal set of values that should be in the session
# in order to pass any filters (e.g. authentication) defined in
# RecordsController. Be sure to keep this updated too.
def valid_session
{"warden.user.user.key" => session["warden.user.user.key"]}
end
describe "GET index" do
it "assigns all records as #records" do
record = Record.create! valid_attributes
get :index, {}, valid_session
assigns(:records).should eq([record])
end
end
describe "GET show" do
it "assigns the requested record as #record" do
record = Record.create! valid_attributes
get :show, {:id => record.to_param}, valid_session
assigns(:record).should eq(record)
end
end
describe "GET new" do
it "assigns a new record as #record" do
get :new, {}, valid_session
assigns(:record).should be_a_new(Record)
end
end
describe "GET edit" do
it "assigns the requested record as #record" do
record = Record.create! valid_attributes
get :edit, {:id => record.to_param}, valid_session
assigns(:record).should eq(record)
end
end
describe "POST create" do
describe "with valid params" do
it "creates a new Record" do
expect {
post :create, {:record => valid_attributes}, valid_session
}.to change(Record, :count).by(1)
end
it "assigns a newly created record as #record" do
post :create, {:record => valid_attributes}, valid_session
assigns(:record).should be_a(Record)
assigns(:record).should be_persisted
end
it "redirects to the created record" do
post :create, {:record => valid_attributes}, valid_session
response.should redirect_to(Record.last)
end
end
describe "with invalid params" do
it "assigns a newly created but unsaved record as #record" do
# Trigger the behavior that occurs when invalid params are submitted
Record.any_instance.stub(:save).and_return(false)
post :create, {:record => {}}, valid_session
assigns(:record).should be_a_new(Record)
end
it "re-renders the 'new' template" do
# Trigger the behavior that occurs when invalid params are submitted
Record.any_instance.stub(:save).and_return(false)
post :create, {:record => {}}, valid_session
response.should render_template("new")
end
end
end
describe "PUT update" do
describe "with valid params" do
it "updates the requested record" do
record = Record.create! valid_attributes
# Assuming there are no other records in the database, this
# specifies that the Record created on the previous line
# receives the :update_attributes message with whatever params are
# submitted in the request.
Record.any_instance.should_receive(:update_attributes).with({'these' => 'params'})
put :update, {:id => record.to_param, :record => {'these' => 'params'}}, valid_session
end
it "assigns the requested record as #record" do
record = Record.create! valid_attributes
put :update, {:id => record.to_param, :record => valid_attributes}, valid_session
assigns(:record).should eq(record)
end
it "redirects to the record" do
record = Record.create! valid_attributes
put :update, {:id => record.to_param, :record => valid_attributes}, valid_session
response.should redirect_to(record)
end
end
describe "with invalid params" do
it "assigns the record as #record" do
record = Record.create! valid_attributes
# Trigger the behavior that occurs when invalid params are submitted
Record.any_instance.stub(:save).and_return(false)
put :update, {:id => record.to_param, :record => {}}, valid_session
assigns(:record).should eq(record)
end
it "re-renders the 'edit' template" do
record = Record.create! valid_attributes
# Trigger the behavior that occurs when invalid params are submitted
Record.any_instance.stub(:save).and_return(false)
put :update, {:id => record.to_param, :record => {}}, valid_session
response.should render_template("edit")
end
end
end
describe "DELETE destroy" do
it "destroys the requested record" do
record = Record.create! valid_attributes
expect {
delete :destroy, {:id => record.to_param}, valid_session
}.to change(Record, :count).by(-1)
end
it "redirects to the records list" do
record = Record.create! valid_attributes
delete :destroy, {:id => record.to_param}, valid_session
response.should redirect_to(records_url)
end
end
end
And a run down of the important bits:
This bit does the programmatic login (ignore the forename and surname attributes, they're specific to the solution I'm building):
before(:each) do
#user = User.create!(forename: "Billy", surname: "Bob", username: "Test", password: "secret!1", email: "test#test.com")
login_user
end
This bit holds session info/key data:
def valid_session
{"warden.user.user.key" => session["warden.user.user.key"]}
end
As diwalak writes, we need to add this to the spec_help.rb file:
include Sorcery::TestHelpers::Rails
And that's it - worked for me anyhow :)

Rails 3.1 Rspec Creating test case validate field for Model

I'm trying to create a test case for User model. Basically, it will validate first_name and last_name to be present.
What I am trying to do is to check whether the error on a specific field is empty or not and it should be empty. However it always fails.
What is the correct way to do this?
Here is my code
On my user_spec.rb
require 'spec_helper'
describe User do
before do
#user = User.new
end
it "must have a first name" do
#user.errors[:first_name].should_not be_empty
end
it "must have a last name" do
#user.errors[:last_name].should_not be_empty
end
end
On my user.rb file
class User < ActiveRecord::Base
validates :first_name, :presence => true
validates :last_name, :presence => true
end
You can test by simply doing this as well:
describe 'validations' do
it { should validate_presence_of :firstname }
it { should validate_presence_of :lastname }
end
Take a look at the shoulda matchers for all such standard Rails Validation.
This way is not just more concise but also takes care of the positive case. Meaning you then dont need to test the scenario mentioned below:
it "passed validations when first_name is set"
user = User.create(:firstname => 'f', :lastname => 'l')
user.errors[:first_name].should be_empty
user.errors[:last_name].should be_empty
end
RSpec supports the notion of an "implicit" subject. If your first argument to the "describe" block is a class, RSpec automatically makes an instance of that class available to your specs. See http://relishapp.com/rspec/rspec-core/v/2-6/dir/subject/implicit-subject.
require 'spec_helper'
describe User do
it "must have a first name" do
subject.should have(1).error_on(:first_name)
end
it "must have a last name" do
subject.should have(1).error_on(:last_name)
end
end
which results in RSpec output (if using --format documentation) of:
User
must have a first name
must have a last name
You can abbreviate it even further if you are content with the RSpec output defaults:
require 'spec_helper'
describe User do
it { should have(1).error_on(:first_name) }
it { should have(1).error_on(:last_name) }
end
which results in:
User
should have 1 error on :first_name
should have 1 error on :last_name

Resources