Test Devise's registration with Rspec and FactoryGirl - ruby-on-rails

Routes
devise_for :users, controllers: { registrations: 'users/registrations' }
Models
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
before_create :set_default_role
enum role_types: [:doctor, :patient]
def set_default_role
set_role :patient unless roles.include? User.role_types[:patient]
end
end
class Patient < User
end
class Doctor < User
end
FactoryGirl
require 'faker'
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "full_name_#{n}" }
address 'valid address'
phone '1234567890'
password 'password'
sequence(:email) { |n| "valid_email_#{n}#example.com" }
end
factory :doctor, class: Doctor, parent: :user do
roles [0, 1]
field
end
factory :patient, class: Patient, parent: :user do
roles [1]
end
end
Registration's spec
require 'spec_helper'
describe Users::RegistrationsController, type: :controller do
before do
#request.env['devise.mapping'] = Devise.mappings[:user]
end
context 'register a patient' do
let(:add_patient) { post :create, patient: FactoryGirl.attributes_for(:patient) }
it { expect { add_patient }.to change(Patient, :count).by(1) }
end
context 'register a doctor' do
let(:add_doctor) { post :create, doctor: FactoryGirl.attributes_for(:doctor) }
it { expect { add_doctor }.to change(Doctor, :count).by(1) }
end
end
When I run the spec, I keep getting this failure for both examples
1) Users::RegistrationsController register a patient should change #count by 1
Failure/Error: it { expect { add_patient }.to change(Patient, :count).by(1) }
expected #count to have changed by 1, but was changed by 0
# ./spec/controllers/registrations_controller_spec.rb:19:in `block (3 levels) in <top (required)>'
My questions are, what causes the failure and how do I debug if my post request is successfully create a user? I know I can use capybara, but in this case I want to make sure if I do the post request for registration, it will register a user.

Related

RSpec testing with followerships / friendships has many through users

I inspired myself with the following link, http://railscasts.com/episodes/163-self-referential-association, but the rspec testing is not coming easy.
user model:
class User < ActiveRecord::Base
# Associations
has_many :followerships
has_many :followers, :through => :followerships
has_many :inverse_followerships, :class_name => "Followership", :foreign_key => "follower_id"
has_many :inverse_followers, :through => :inverse_followerships, :source => :user
end
followership model:
class Followership < ActiveRecord::Base
belongs_to :user
belongs_to :follower, :class_name => "User"
end
followerhip factory:
FactoryGirl.define do
factory :followership do
user_id 1
follower_id 1
end
end
followerships controller:
class FollowershipsController < InheritedResources::Base
def create
#followership = current_user.followerships.build(:follower_id => params[:follower_id])
if #followership.save
flash[:notice] = "Following."
redirect_to root_url
else
flash[:error] = "Unable to follow."
redirect_to root_url
end
end
def destroy
#followership = current_user.followerships.find(params[:id])
#followership.destroy
flash[:notice] = "Removed followership."
redirect_to current_user
end
end
folowerships controller spec (this is all wrong):
require 'rails_helper'
describe FollowershipsController do
let!(:followership) { create(:followership) }
let!(:follower) { followership.follower }
let!(:user) { create(:user) }
before do
sign_in :user, user
end
describe "#create" do
it "saves the followership" do
post :create, followership: { follower_id: follower }
expect(response).to redirect_to(root_path)
expect(assigns(:followership).followership.followers).to eq(user)
expect(flash[:notice]).to eq("Following.")
end
it "fails to save followership" do
expect(post :create, followership: { follower_id: follower }).to redirect_to(root_path)
expect(flash[:notice]).to eq("Unable to follow.")
end
end
describe "#destroy" do
it "deletes the followership" do
expect {
delete :destroy, id: follower
}.to change(Followership, :count).by(-1)
expect(flash[:notice]).to eq("Removed followership.")
end
end
end
Error from followerships controller Rspec
FollowershipsController
#destroy
deletes the followership (FAILED - 1)
#create
saves the followership (FAILED - 2)
fails to save followership (FAILED - 3)
Failures:
1) FollowershipsController#destroy deletes the followership
Failure/Error: delete :destroy, id: follower
ActionController::UrlGenerationError:
No route matches {:action=>"destroy", :controller=>"followerships", :id=>nil}
2) FollowershipsController#create saves the followership
Failure/Error: expect(assigns(:followership).followership.followers).to eq(user)
NoMethodError:
undefined method `followership' for #<Followership:0x00000109f69780>
3) FollowershipsController#create fails to save followership
Failure/Error: expect(flash[:notice]).to eq("Unable to follow.")
expected: "Unable to follow."
got: "Following."
(compared using ==)
Thanks for the help :)
The let command uses lazy evaluation, so these records are not actually created until called. Use the let! syntax to ensure they're created before your tests run:
let!(:followership) { create(:followership) }
let!(:follower) { followership.follower }
let!(:user) { create(:user) }
Make sure your validations also only allow creation of a following if it doesn't already exist for that pair of users:
class Followership < ActiveRecord::Base
validates_uniqueness_of :user_id, scope: :follower_id
Also, it's not guaranteed that the follower/followership relationships will belong to user since user doesn't necessarily have an id of 1.
Finally, assigns is a method, so the syntax should be assigns(:followership) not assigns[:followership]

after association between two models error on a third : ActiveRecord::RecordInvalid: Validation failed: Email has already been taken

I just concluded this model associations between users (using devise to manage users), wall and posts. Before I tried the user and wall association the following error didn't exist.
The output failure is:
Post
Scopes
.most_recent
returns all posts ordered from the youngest to the oldest (FAILED - 1)
Failures:
1) Post Scopes .most_recent returns all posts ordered from the youngest to the oldest
Failure/Error: let!(:post) { create(:post, created_at: Date.today) }
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
# ./spec/models/post_spec.rb:16:in `block (3 levels) in <top (required)>'
# -e:1:in `<main>'
Failed examples:
rspec ./spec/models/post_spec.rb:20 # Post Scopes .most_recent returns all posts ordered from the youngest to the oldest
My models:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Associations
has_one :wall, dependent: :destroy
end
class Wall < ActiveRecord::Base
# Associations
has_many :posts, dependent: :destroy
belongs_to :user
# Validations
validates :user, presence: true
end
class Post < ActiveRecord::Base
# Associations
belongs_to :wall
# Scopes
scope :most_recent, -> { order(created_at: :desc) }
# Validations
validates :content, :wall, presence: true
end
My post_spec:
require 'rails_helper'
RSpec.describe Post, type: :model do
let(:post) { build(:post) }
describe 'Validations' do
it 'has a valid factory' do
expect(post).to be_valid
end
it { should validate_presence_of(:content) }
end
describe "Scopes" do
let!(:older_post) { create(:post, created_at: Date.today - 2.month) }
let!(:post) { create(:post, created_at: Date.today) }
describe ".most_recent" do
it "returns all posts ordered from the youngest to the oldest" do
expect(Post.most_recent.first).to eq(post)
expect(Post.most_recent.last).to eq(older_post)
end
end
end
end
My post factory:
FactoryGirl.define do
factory :post do
content 'post text'
wall
end
end
any hint?
This is just a guess, but your User factory probably isn't generating unique email addresses. FactoryGirl lets you define a sequence, which will ensure uniqueness validation for your test users:
FactoryGirl.define do
sequence :email do |n|
"person#{n}#example.com"
end
end
factory :user do
email
end
You can read more in the documentation here: http://www.rubydoc.info/gems/factory_girl/file/GETTING_STARTED.md#Sequences

How to write an Rspec controller test with authentication?

I have two models:
# app/models/user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_many :teams, dependent: :destroy
end
and
# app/models/team.rb
class Team < ActiveRecord::Base
belongs_to :user
validates_presence_of :user
end
I am trying to test my TeamsController using Rspec and factory_girl_rails.
Before I can create a new Team I need an authenticated User.
I created a :user factory:
FactoryGirl.define do
factory :user do
first_name "John"
last_name "Doe"
email {|n| "email#{n}#email.com" }
mobile_phone "1235551234"
company_name "Widgets Inc."
password "password"
end
end
Here are the relevant parts of teams_controller_spec.rb:
require 'rails_helper'
RSpec.describe TeamsController, type: :controller do
# 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
# TeamsController. Be sure to keep this updated too.
let(:valid_session) { {} }
describe "GET #index" do
it "assigns all teams as #teams" do
user = FactoryGirl.create(:user)
team = Team.create!(name: "New Team Name", user: user)
get :index, {}, valid_session
expect(assigns(:teams)).to eq([team])
end
end
end
The test is failing:
1) TeamsController GET #index assigns all teams as #teams
Failure/Error: get :index, {}, valid_session
NoMethodError:
undefined method `authenticate' for nil:NilClass
I don't understand how I need to populate :valid_session so that the test will pass. I thought I'd have to explicitly call an authenticate method but that might not be true. I'm trying to test the Team controller... not User authentication.
Any advice would be much appreciated.
I'd do this in your rails_helper:
module ControllerMacros
def sign_me_in
before :each do
#request.env['devise.mapping'] = Devise.mappings[:user]
#current_user = FactoryGirl.create(:user)
sign_in :user, #current_user
end
end
end
Rspec.configure do |config|
#other rspec stuff
config.include FactoryGirl::Syntax::Methods
config.extend ControllerMacros, type: :controller
config.include Devise::Test::ControllerHelpers, type: :controller
end
Then in your controller spec, (provided you're requiring your rails_helper) you can just to sign_me_in whenever you want to be signed in and not bother about the valid_session:
RSpec.describe TeamsController, type: :controller do
sign_me_in
#etc...
end
However in your specific case you want to know who you're signed in as, so you can do this:
RSpec.describe TeamsController, type: :controller do
describe "GET #index" do
it "assigns all teams as #teams" do
user = FactoryGirl.create(:user)
team = Team.create!(name: "New Team Name", user: user)
#request.env['devise.mapping'] = Devise.mappings[:user]
sign_in :user, user
get :index
expect(assigns(:teams)).to eq([team])
end
end
end
The devise mappings line may not be required in your case, but can't say without inspecting your full app.

Testing User visiting his own profile with rspec capybara, Warden gems on rails

I'm new to Ruby on rails and programming in general.
In an assignment I'm doing, I was asked to create a test where a User visits his on profile.
Rspec, Devise and capybara gems are installed.
Here is my profiles_spec:
require 'rails_helper'
describe "Visiting profiles" do
include TestFactories
before do
#user = authenticated_user
#post = associated_post(user: #user)
#comment = Comment.new(user: #user, post: #post, body:"A comment")
allow(#comment).to receive(:send_favorite_emails)
#comment.save
user = FactoryGirl.create(:user)
login_as(user, :scope => :user)
end
describe "not signed in" do
it "shows profile" do
visit user_path(#user)
expect(current_path).to eq(user_path(#user))
expect(page).to have_content(#user.name)
expect(page).to have_content(#post.title)
expect(page).to have_content(#comment.body)
end
end
describe "user visting own profile" do
it "shows profile" do
visit user_path(current_user)
expect(current_path).to eq(user_path(user))
expect(page).to have_content(user.name)
expect(page).to have_content(#post.title)
expect(page).to have_content(#comment.body)
end
end
end
Here is my TestFactories:
module TestFactories
include Warden::Test::Helpers
Warden.test_mode!
def associated_post(options = {})
post_options = {
title: 'Post title',
body: 'Post bodies must be pretty long.',
topic: Topic.create(name: 'Topic name',description: 'the description of a topic must be long'),
user: authenticated_user
}.merge(options)
Post.create(post_options)
end
def authenticated_user(options = {})
user_options = { email: "email#{rand}#fake.com", password: 'password'}.merge(options)
user = User.new( user_options)
user.skip_confirmation!
user.save
user
end
FactoryGirl.define do
factory :user do
email 'test#example.com'
password 'f4k3p455w0rd'
user = FactoryGirl.create(:user)
login_as(user, :scope => :user)
# if needed
# is_active true
end
end
end
Here is my User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_many :votes, dependent: :destroy
has_many :favorites, dependent: :destroy
mount_uploader :avatar, AvatarUploader
def admin?
role == 'admin'
end
def moderator?
role == 'moderator'
end
def favorited(post)
favorites.where(post: post.id).first
end
def voted(post)
votes.where(post: post.id).first
end
end
When I run the profiles test, I get this error:
`<module:TestFactories>': uninitialized constant TestFactories::FactoryGirl (NameError)
I'm not sure if i'm using warden the right way.
Thank you.
The error is because you have not included FactoryGirl In your TestFactories-module.
Your factory looks pretty messy.
In my projects I have a folder structure with spec/factories where I put factories.
For example I would name this users.rb:
FactoryGirl.define do
factory :user do
email
password '12345678'
end
end
To skip confirmation you can add:
confirmed_at: { Time.zone.now}
In a separate factory called shared
I put:
sequence(:email) { |n| "name#{n}#domain.se" }
Then when I want a user in a test I use
let(:user) { FactoryGirl.create(:user
And you should be able to use
login_as
I'm not sure how that work because I mostly use CanCanCommunity.
Sorry for messy post, written on phone.
The uninitialized constant...FactoryGirl error message says that FactoryGirl isn't available.
The options are to install and configure the factory girl gem, or to avoid it until its needed.
For this spec, it looks like factory girl isn't adding anything, so consider removing these references to it (and avoid installing the factory girl gem for now):
The line user = FactoryGirl.create(:user) from profiles_spec.rb
The entire FactoryGirl.define do...end block in TestFactories
It appears that the existing #user object is adequate for what the spec needs. user and current_user don't seem to do anything that #user couldn't be used for. Apologies if I've missed something.

Factory_girl create gives validation error

I'm trying to test a destroy action in my rails application. I use Factory_girl to create objects.
When creating a company object it's mandatory to add a user relation. But this is where I get a fail when I try to create a Company with Factory_girl.
user_spec.rb
describe User do
before(:each) { #user = User.new(email: 'user#example.com') }
subject { #user }
it { should respond_to(:email) }
it { should respond_to(:companies) }
it "#email returns a string" do
expect(#user.email).to match 'user#example.com'
end
describe "company associations" do
let!(:a_company) do
FactoryGirl.create(:company, user: #user)
end
it {should have_many :companies, :dependent => :destroy}
end
end
factory.rb
FactoryGirl.define do
factory :user do
confirmed_at Time.now
name "Test User"
email "test#example.com"
password "please123"
trait :admin do
role 'admin'
end
end
factory :company do
name "Total Inc."
user :user
end
end
model/user.rb
class User < ActiveRecord::Base
has_many :companies, dependent: :destroy
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :invitable, :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
end
model/company.rb
class Company < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
end
The error I get:
Failures:
1) User company associations
Failure/Error: FactoryGirl.create(:company, user: #user)
ActiveRecord::RecordInvalid:
Validation failed: User can't be blank
EDIT
I followed the advise from below and now I can create but get following error:
Failure/Error: it {should have_many :companies, :dependent => :destroy} expected #<User:0x007fc7b7ce08c0> to respond to has_many?
The first error, Validation failed: User can't be blank, is a result of not having saved the #user object. You can fix the error by saving the object before you call create:
let!(:a_company) do
#user.save
FactoryGirl.create(:company, user: #user)
end
The second error, Failure/Error: it {should have_many :companies, :dependent => :destroy} expected #<User:0x007fc7b7ce08c0> to respond to has_many? is a testing error - the production code works fine. To fix your test try one of the following options:
Use Shoulda
it {should have_many(:companies).dependent(:destroy)}
Use FactoryGirl
it 'Expects user to have many companies' do
expect{#user.companies}.to_not raise_error
end

Resources