How to test Rspec in controller - ruby-on-rails

In controller,
def admin_search
#admins = User.find(:all,:joins=>[:roles],:conditions=>["name IN (?) and email like '#{params[:email]}%'",["content team","ops team"]]).paginate(:page => params[:page], :per_page => 10)
end
please suggest me some code in rspec

First of all, it's better to extract find(:all, ...) call to User model. Call it search, for instance.
class User < ActiveRecord::Base
scope :search_by_email, lambda { |email|
joins(:roles).where(["name IN (?) and email like '#{email}%'",["content team","ops team"]])
}
end
Use it in the controller then:
def admin_search
#admins = User.search_by_email(params[:email]).paginate(:page => params[:page], :per_page => 10)
end
Now, you can test the search_by_email method in isolation - check, that it returns result for "content team" and "ops team" only, correctly works with empty email string and so on.
I don't think you have to test paginate method, as it should be already tested in kaminari, will_paginate or whatever you use. But if you want to be sure, that it is being called, than you can use mock expectations (should_receive) in the controller specs.
EDIT: How the specs could look like
describe User do
describe ".search_by_email" do
let(:content_team) { Role.create! name: "content team" }
let(:ops_team) { Role.create! name: "ops team" }
let(:another_team) { Role.create! name: "another team" }
it "should search in content team" do
content_team_user = User.create! email: "joe.black#example.com", roles: [content_team]
User.search_by_email("black").should == [content_team_user]
end
it "should search in ops team" do
ops_team_user = User.create! email: "joe.black#example.com", roles: [ops_team]
User.search_by_email("black").should == [ops_team_user]
end
it "should not search in other teams" do
other_team_user = User.create! email: "joe.black#example.com", roles: [another_team]
User.search_by_email("black").should == []
end
it "should not search by empty string" do
content_team_user = User.create! email: "joe.black#example.com", roles: [content_team_user]
User.search_by_email("").should == []
User.search_by_email(nil).should == []
end
# more specs for search...
end
end
describe UsersController do
describe "admin search" do
let(:admin_user) { double(:admin_user).as_null_object }
let(:search_string) { 'joe' }
it "should search for admin users" do
User.should_receive(:search_by_email).with(search_string).and_return([admin_user])
get :admin_search, email: search_string
assigns(:admins).should == [admin_user]
end
end
end

Related

Passing variable between multiple contexts with Rspec

I'm writing some tests using FactoryGirl and Rspec.
spec/factories/students.rb:
FactoryGirl.define do
factory :student do
end
factory :student_with_profile_and_identity, class: 'Student' do
after(:create) do |student|
create(:profile, profileable: student)
create(:student_identity, student: student)
end
end
end
spec/factories/profiles.rb:
FactoryGirl.define do
factory :profile do
birthday { Faker::Date.birthday(15, 150) }
sequence(:email) { |i| "profile_#{i}#email.com" }
first_name { Faker::Name.first_name }
last_name { Faker::Name.first_name }
password { Faker::Internet.password(6, 72, true, true) }
end
end
spec/factories/student_identities.rb:
FactoryGirl.define do
factory :student_identity do
provider { ['facebook.com', 'google.com', 'twitter.com'].sample }
uid { Faker::Number.number(10) }
end
end
spec/requests/authorizations_spec.rb:
require 'rails_helper'
RSpec.describe 'Authorizations', type: :request do
describe 'POST /v1/authorizations/sign_in' do
let!(:student) { create(:student_with_profile_and_identity) }
context 'when the request is valid' do
subject do
post '/v1/authorizations/sign_in',
params: credentials
end
context "user signs up via social network" do
let(:credentials) do
{
authorization: {
student: {
profile_attributes: {
email: student.profile.email
},
student_identities_attributes: {
provider: student.student_identities[0].provider,
uid: student.student_identities[0].uid
}
}
}
}
end
it 'returns an authentication token' do
subject
p "1 student.profile.inspect #{student.profile.inspect}"
expect(json['token']).to(be_present)
end
end
context 'when the user has already an account' do
let(:credentials) do
{
authorization: {
email: student.profile.email,
password: student.profile.password
}
}
end
it 'returns an authentication token' do
p "2 student.profile.inspect #{student.profile.inspect}"
subject
expect(json['token']).to(be_present)
end
end
end
end
end
Almost all tests are passing... the problem is that:
It's creating a new student in every context. I'd expect the let!(:student) { ... } to be something like "singleton", in other words, once it's created/defined here let!(:student) { create(:student_with_profile_and_identity) } it won't be called anymore.
Ex: the logs are like this:
"1 student.profile.inspect #<Profile id: 1, email: \"profile_1#email.com\", profileable_type: \"Student\", profileable_id: 1>"
"2 student.profile.inspect #<Profile id: 2, email: \"profile_2#email.com\", profileable_type: \"Student\", profileable_id: 2>"
While I'd expect the instances to be the same.
Am I missing something?
In RSpec, let and let! are the same thing, except that let is lazy and let! is eager:
Use let to define a memoized helper method. The value will be cached across multiple calls in the same example but not across examples.
Note that let is lazy-evaluated: it is not evaluated until the first time the method it defines is invoked. You can use let! to force the method's invocation before each example.
If you want something to persist through all examples, you can use a before hook...before(:context) sounds like it might be what you're wanting. You might be able to setup a helper method that memoizes in a before block, to avoid having to use an instance variable everywhere (per this comment):
def student
#student ||= create(:student_with_profile_and_identity)
end
before(:context) do
student # force student creation
end

What could be possible test cases for this controller action and how can i handle if else conditions. Using minitest framework in RubyonRails

I am new to writing test cases and I cant figure out the scenarios of writing tests. For example there are too many if else conditions in controller how would I write cases for these conditions. Below is my registration controller. I am using rails minitest framework for rails 3.2.1 version.
def create
invitation_token = params["invitation_token"]
#Check if the user exists yet based on their e-mail address.
user = User.find_by_email(params[:user][:email])
omni = session[:omniauth] || params[:omniauth]
theme_id = nil
theme_layout_id = nil
theme_style_id = nil
begin
omni = JSON.parse omni if omni
rescue => e
# if we got here, the omni is invalid!!
return redirect_to '/login'
end
#Did we find a user yet? If not, perform the following.
if user.nil? && !invitation_token.present?
client = Client.find_or_create_by_name(name: params[:user][:username])
#p client.errors
if client.present?
user = User.new
app_url = ApplicationUrl.find_by_domain_url(request.host_with_port)
user.apply_omniauth(omni)
user.email = params[:user][:email]
user.username = params[:user][:username]
user.client_id = client.id
#Assign the user/client the Free plan by default.
plan = ClientPlan.find_or_create_by_client_id(client_id: client.id, plan_id: 1, plan_billing_cycle_id: 1, start_date: Date.today, is_paid: 1, isactive: 1)
#Set the client settings to the defaults for a Free (non business plan) user.
ClientSetting.create(client_id: client.id, is_billboard_enabled: 0, is_tweetback_enabled: 0, is_conversations_enabled: 0)
#Set the client environment link.
ClientsEnvironment.create(environment_id: environment.id, client_id: client.id)
unless params[:user][:theme_id].nil?
theme_id = params[:user][:theme_id]
puts "theme id: " + theme_id.to_s
end
unless params[:user][:theme_layout_id].nil?
theme_layout_id = params[:user][:theme_layout_id]
puts "theme layout id: " + theme_layout_id.to_s
end
unless params[:user][:theme_style_id].nil?
theme_style_id = params[:user][:theme_style_id]
puts "theme style id: " + theme_style_id.to_s
end
#Create an application for the client.
Application.find_or_create_by_client_id(
client_id: client.id,
name: params[:user][:username],
callback_url: "#{request.host_with_port}",
application_url_id: app_url.id
)
#Create the default feed for the client.
Feed.find_or_create_by_client_id(
client_id: client.id,
name: 'My Feed',
token: SecureRandom.uuid,
theme_id: theme_id,
theme_style_id: theme_style_id,
theme_layout_id: theme_layout_id
)
if user.save
#Remember me?
if params[:remember_me]
user.remember_me!
end
client = user.client
client.update_attribute(:owner_user_id, user.id)
schedule_reminder_email(user)
#Create the users Profile
Profile.find_or_create_by_user_id(
user_id: user.id,
fullname: params[:user][:fullname],
username: params[:user][:username]
)
record_event_profile(user,params[:user][:fullname],params[:remember_me])
end
end
elsif user.nil? && invitation_token.present?
user = User.new
invite = Invite.find_by_token(invitation_token)
if invite.present?
client = invite.client
user.apply_omniauth(omni)
user.email = params[:user][:email]
user.username = params[:user][:username]
user.client_id = client.id
user.can_curate = false
user.can_publish = false
if user.save
#Remember me?
if params[:remember_me]
user.remember_me!
end
#Create the users Profile
Profile.find_or_create_by_user_id(
user_id: user.id,
fullname: params[:user][:fullname],
username: params[:user][:username]
)
record_event_profile(user,params[:user][:fullname],params[:remember_me])
invite.update_attributes({invite_accepted_at: Time.now, name: user.profile.try(:fullname)})
end
else
return redirect_to root_path
end
else
#If a user already exists for the email address then this must just be a new social network account for this user.
token = omni['credentials']['token']
token_secret = ""
user.relatednoise_authentications.create!(
provider: omni['provider'],
uid: omni['uid'],
token: token,
token_secret: token_secret
) if user.present?
end
#Create an entry in Socialnetworkaccounts for this user to associate them to their social login/account.
create_sna(omni, user)
#SignupNotifier.init_notify(user).deliver
begin
ApiConnector.new("#{API_URL}/notify_us/#{user.id}")
rescue => e
Airbrake.notify_or_ignore(e, {})
end
unless user.new_record?
session[:omniauth] = nil
session[:omniauth_auth] = nil
#reset_invite_token
end
session[:user_id] = user.id
record_event_signup(user)
back_if_coming_from_wix(params[:wix_appid], user)
sign_in_and_redirect user if !params[:wix_appid].present?
end
so far i have written this. Not sure if this is the way to write test cases.
require 'test_helper'
class RegistrationsControllerTest < ActionController::TestCase
fixtures :users
def setup
#params = {"omniauth"=>"{\"provider\":\"twitter\",\"uid\":\"167003011\",\"credentials\":{\"token\":\"167003011-ZqnlBsCZlFjymanQ6gQ2ggD7a2tAESuUVlygw0WN\",\"secret\":\"idVWQgR79HOKmZfuNtVtxvzWzGH5plJlxdEksxyuHgH5S\"}}","user"=>{"fullname"=>"Tommy", "email"=>"Tom#moody.com", "username"=>"tommy", "theme_id"=>"", "theme_style_id"=>"", "theme_layout_id"=>""}}
#invite = invites(:arvind_invite)
end
def test_new
get :new
assert_response :success
end
def test_create_for_client_plan
assert_difference ->{ ClientPlan.count }, +1 do
post :create, #params
end
end
def test_create_for_client_setting
assert_difference ->{ ClientSetting.count }, +1 do
post :create, #params
end
end
def test_create_for_client_environment
assert_difference -> {ClientsEnvironment.count}, +1 do
post :create, #params
end
end
def test_create_for_application
assert_difference -> {Application.count}, +1 do
post :create, #params
end
end
def test_create_for_user
assert_difference -> {User.count}, +1 do
post :create, #params
end
end
def test_create_for_feed
assert_difference -> {Feed.count}, +1 do
post :create, #params
end
end
def test_create_for_profile
assert_difference -> {Profile.count}, +1 do
post :create, #params
end
end
def test_create_for_sna
assert_difference -> {Socialnetworkaccount.count}, +1 do
post :create, #params
end
end
def test_create_for_user_with_invitation
assert_difference -> {User.count}, +1 do
post :create, #params.merge({invitation_token: #invite.token})
end
end
end
This is my test helper file.
ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
class ActiveSupport::TestCase
include Devise::TestHelpers
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
# -- they do not yet inherit this setting
fixtures :all
def host_with_port
#request.host_with_port = "localhost:3000"
end
# Add more helper methods to be used by all tests here...
end

Rspec testing ransack always returning all instances

I would like to do a specific search using ransack but my test always returns all instances.
My test:
RSpec.describe UsersController, type: :controller do
describe "GET #index" do
context 'ransack search by email' do
let!(:user1) { create(:user, email: 'user1#example.com') }
let!(:user2) { create(:user, email: 'user2#example.com') }
context 'finds specific user' do
before { get :index, q: '2' }
it "should find just one user" do
expect(assigns(:users).first).to eq [user2]
end
it { should respond_with(:success) }
it { should render_template(:index) }
end
My controller:
class UsersController < ApplicationController
def index
#q ||= User.ransack(params[:q])
#users = #q.result(distinct: true)
end
end
What am I doing wrong?
The param q: should be like
q: {email_cont: '2'}

Rspec testing a controller using from to?

Tested in browser and works fine. Test error says "expected result to have changed from 0 to 1, but did not change". Is this a factory issue or rspec issue? Why is it not changing?
Error:
Failures:
1) ShortLinksController Short links controller Clicking a short link increments the click counter by 1
Failure/Error: expect{ get :url_dispatch, { id: short_link.short_link } }.to change{short_link.click_counter}.from(0).to(1)
expected result to have changed from 0 to 1, but did not change
# ./spec/controllers/short_links_controller_spec.rb:34:in `block (4 levels) in <top (required)>'
Rspec:
it "increments the click counter by 1" do
short_link = create(:short_link)
expect{ get :url_dispatch, { id: short_link.short_link } }.to change{short_link.click_counter}.from(0).to(1)
end
Controller:
def url_dispatch
id = params[:id]
record = ShortLink.where(["short_link = ?", id]).first
if record.update(click_counter: record.click_counter + 1)
redirect_to record.redirect_to
else
render '/not_found'
end
end
Factory:
FactoryGirl.define do
factory :short_link do
redirect_to "http://google.com"
title "This is the google page"
short_link "xGh7u"
click_counter 0
owner Owner.create!(first_name: "Bob", last_name: "Diller", email: "bdiller#example.com")
end
end
per Fab's request, here is how I'm currently working around the issue.
context 'save invocations' do
before(:each) do
#org = create(:organization)
user = create(:user, organization: #org, is_admin: true)
sign_in user
end
it 'valid scenario' do
user2 = create(:user, organization: #org, is_admin: false)
put :update, id: user2, user: { is_admin: true }
user2.reload
expect(response).to have_http_status(204)
expect(user2.is_admin).to eq true
end
end
Here I'm calling user2.reload in order to get the updated attributes from the user2 factory.
I don't know why the expect{} syntax doesn't work for factories but you could refactor your code like this:
it "increments the click counter by 1" do
short_link = create(:short_link)
count = short_link.click_counter
get :url_dispatch, { id: short_link.short_link }
short_link.reload
expect(short_link.click_counter).to eq count + 1
end
Again I'm not saying this is best practice, I just couldn't find anything in the FactoryGirl documentation regarding RSpec 3 expect syntax in controllers that update attributes.

rails rspec access other tables when testing

I'm pretty new to rails and find myself having some problems with understanding the relation between my database tables (i guess?).
My problem is the following:
I've got a Users table, containing information about users including their email addresses and another table containing "games" i'd like to manage those players played.
When players want to submit their games, they have to specify the users participating in the game by their email addresses. I'd like to validate whether or not those players really exist.
My Game model:
class Game < ActiveRecord::Base
attr_accessible :caster1, :caster2, :faction1, :faction2, :player1, :player2, :points, :won
before_save { |game| player1 = player1.downcase }
before_save { |game| player2 = player2.downcase }
validate :existence_of_both_players
...
(some more validations)
...
private
def existence_of_both_players
User.exists?(:email => :player1.downcase) && User.exists?(:email => :player2.downcase)
end
end
My test case is the following:
require 'spec_helper'
describe Game do
before do
#game = Game.new( player1: "foobar#example.com",
faction1: "Some faction",
caster1: "Some caster",
player2: "bazbaz#example.com",
faction2: "Some other faction",
caster2: "Some other caster",
points: 35,
won: true)
end
...
(some other specs)
...
describe "saving a game" do
let(:player1) { User.create(name: "Example1",
email: "example1#foo.bar",
password: "foobar",
password_confirmation: "foobar") }
let(:player2) { User.create(name: "Example2",
email: "example2#foo.bar",
password: "foobar",
password_confirmation: "foobar") }
describe "with invalid players" do
describe "when both players do not exist" do
before { #game.player1 = #game.player2 = "some_wrong_user#example.com" }
it { should_not be_valid }
end
describe "when player1 does not exist" do
before { #game.player2 = player2 }
it { should_not be_valid }
end
describe "when player2 does not exist" do
before { #game.player1 = player1 }
it { should_not be_valid }
end
end
describe "with valid players" do
before do
#game.player1 = player1
#game.player2 = player2
end
it { should be_valid }
end
end
end
(Sorry for the mass of code, I just thought it would help).
My test are failing, and I'm pretty sure it's obvious why, sadly not for me.
Failures:
1) Game saving a game with invalid players when both players do not exist
Failure/Error: it { should_not be_valid }
expected valid? to return false, got true
# ./spec/models/game_spec.rb:108:in `block (5 levels) in <top (required)>'
...
I really can not figure out, why this is not working. I read the rails book and watched several screencasts, but none of them explains my problem properly.
Since this is my first post on stackoverflow, please let me know, if this post is too verbose. Thanks in advance.
A custom validator method should call errors.add to signal an error. So in your case something like:
def existence_of_both_players
player_one_exists = User.find_by_email :player1.downcase
player_two_exists = User.find_by_email :player2.downcase
unless player_one_exists and player_two_exists
errors.add :game, "both players need to exist"
end
end
Read more in the guide for ActiveRecord Validations and Callbacks.

Resources