I just added the Rolify gem, and am running some rspec tests.
2 tests are as follows:
describe "roles" do
before(:each) do
#user = FactoryGirl.create(:user)
end
it "should not approve incorrect roles" do
#user.add_role :moderator
#user.has_role? :admin
should be_false
end
it "should approve correct roles" do
#user.add_role :moderator
#user.has_role? :moderator
should be_true
end
end
The test result is:
1) User roles should not approve incorrect roles
Failure/Error: should be_false
expected: false value
got: #<User id: nil, email: "", encrypted_password: "", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, confirmation_token: nil, confirmed_at: nil, confirmation_sent_at: nil, name: nil, position: nil, location: nil, admin: false, archived: false, public_email: false, created_at: nil, updated_at: nil>
# ./spec/models/user_spec.rb:70:in `block (3 levels) in <top (required)>'
Finished in 1.37 seconds
7 examples, 1 failure
Failed examples:
rspec ./spec/models/user_spec.rb:67 # User roles should not approve incorrect roles
Randomized with seed 13074
factories.rb
FactoryGirl.define do
factory :user do
sequence(:name) {|n| "Example User #{n}"}
sequence(:email) {|n| "email#{n}#program.com" }
position "Regular"
location "Anywhere, USA"
public_email false
password "foobar"
password_confirmation "foobar"
confirmed_at Time.now
end
end
How is the first test is failing with a nil object, but the second is passing?
EDIT
Upon further inspection, any test for should be_true passes, and any test for should be_false fails, regardless of whether the added role matches the checked role.
When your tests do should be_true what is happening is the should call is being delegated to the subject object (see RSpec docs for implicit receiver). In this case, your subject object is a User instance that has not yet been saved to the database. If your user_spec.rb file starts with describe User do, RSpec is automatically providing this default subject of User.new (see RSpec docs for implicit subject).
What this means is that your tests are essentially doing User.new.should be_true and User.new.should be_false. Since a User object will always evaluate to true, the should be_true test will always pass (although probably not for the reason you wanted it to) and the should be_false will always fail.
Based on the way your tests are written, maybe you meant something more like this:
describe "roles" do
before(:each) do
#user = FactoryGirl.create(:user)
end
it "should not approve incorrect roles" do
#user.add_role :moderator
#user.has_role?(:admin).should be_false
end
it "should approve correct roles" do
#user.add_role :moderator
#user.has_role?(:moderator).should be_true
end
end
I'm assuming that the example group is actually nested in a group declared with describe User do, yes?
The problem is that the last line of each example reads should xxx, but should has no receiver, so RSpec is substituting an instance of User for you.
What you want is:
describe User do
describe "roles" do
before(:each) do
#user = FactoryGirl.create(:user)
end
it "should not approve incorrect roles" do
#user.add_role :moderator
#user.has_role?(:admin).should be_false
end
it "should approve correct roles" do
#user.add_role :moderator
#user.has_role?(:moderator).should be_true
end
end
end
HTH,
David
Related
I set up Pundit to guard a bunch of request paths, and it's working fine. In particular, if I hit /api/users/:id with a PATCH request, passing the relevant parameters, I get a 403 if I'm not authenticated. Then I wrote this spec
context 'When logged out' do
describe 'user update' do
before(:each) do
#new_user = FactoryBot.create(:user, password: 'testpassword')
end
it 'fails with PATCH' do
patch "/api/users/#{#new_user.id}", params: { given_name: 'Testing Alice' }
expect(response).to have_http_status(:forbidden)
end
end
end
but when I run rspec, I get the following failure:
1) When logged out user update fails with PATCH
Failure/Error: authorize #user
Pundit::NotAuthorizedError:
not allowed to update? this #<User id: 24, email: "nolanschinner#schuster.net", given_name: "Deja", family_name: "Schmeler", role: "USER", password_digest: "$2a$04$3lhKjBj2DfLymYnTfhDZV.IrlhPPxsPHIe.hI0lHdb1...", created_at: "2018-12-07 15:08:00", updated_at: "2018-12-07 15:08:00", verification_token: nil, email_verified: false, gender: nil>
# /Users/morpheu5/.rvm/gems/ruby-2.5.1/gems/pundit-2.0.0/lib/pundit.rb:209:in `authorize'
# ./app/controllers/api/v1/users_controller.rb:55:in `update'
# ...
The method being tested is right here.
As far as I can tell, Pundit raises the exception and this throws rspec into despair. How do I write this test so that it actually works?
The subject is a bit old but, for those still searching for the answer, you should write something like:
context 'When logged out' do
describe 'user update' do
before(:each) do
#new_user = FactoryBot.create(:user, password: 'testpassword')
end
it 'fails with PATCH' do
expect{patch "/api/users/#{#new_user.id}", params: { given_name: 'Testing Alice' }}.to raise_error(Pundit::NotAuthorizedError)
end
end
end
I'm trying to get into TDD and i'm currently struggling with this error message:
UsersController GET 'show' should find the right user
Failure/Error: expect(user).to eq(#user)
expected: #<User id: 1, email: "example#example.com", encrypted_password: "$2a$04$AVGGS0XU1Kjmbdc/iZ86iOq2f4k992boP7xfqcg2nl6...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, created_at: "2014-06-08 19:43:41", updated_at: "2014-06-08 19:43:41", name: "Test User", confirmation_token: nil, confirmed_at: "2014-06-08 19:43:41", confirmation_sent_at: nil, unconfirmed_email: nil, account_id: 1, notify: nil>
got: nil
(compared using ==)
# ./spec/controllers/users_controller_spec.rb:28:in `block (3 levels) in <top (required)>'
Here's the particular test:
require 'rails_helper'
include Devise::TestHelpers
describe UsersController do
before (:each) do
#user = FactoryGirl.create(:user)
sign_in #user
end
describe "GET 'show'" do
it "should find the right user" do
get :show, :id => #user.id
puts "user = #{#user.inspect}"
user = assigns(:user)
#assigns(:user).should == #user
puts "assigns user = #{assigns(:user)}"
expect(user).to eq(#user)
end
end
end
Here's the controller:
class UsersController < ApplicationController
...
def show
authorize! :show, #user, :message => 'Not authorized as an administrator.'
#user = current_account.users.find(params[:id])
end
...
end
application_controller.rb:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
check_authorization :unless => :devise_controller?
before_filter :authenticate_user!, :validate_subdomain
helper_method :subdomain, :current_account
layout :set_layout
# This will redirect the user to your 404 page if the account can not be found
# based on the subdomain. You can change this to whatever best fits your
# application.
def validate_subdomain
current_account
end
def current_account
#current_account ||= Account.where(:subdomain => subdomain).first
#puts #current_account
if #current_account.nil?
redirect_to('/accounts/invalid_site')
return
end
#current_account
end
def subdomain
request.subdomain
end
...
end
user factory:
FactoryGirl.define do
factory :user do
name 'Test User'
account_id 1
email 'example#example.com'
password 'changeme'
password_confirmation 'changeme'
# required if the Devise Confirmable module is used
confirmed_at Time.now
end
end
How can I get around this error message? I have a feeling it has something to do with before_filters, but that's just a guess.
Do you have before_filter on your users controller where you need authentication to use the show method? Your before block is making two calls, creating a user and then "sign_in", some kind of authentication. But your test is just to see if the user was created and the show method returns the correct user record with the passed id.
So I would break down your before block. Start by testing that your Factory is being created successfully:
it 'has a valid factory' do
FactoryGirl.create(:user).should eq(true) # or should be_valid..
end
If that passes, I'd comment out any constraint on your users controller (temporarily) for the sign_in method, and test if show receives the id and returns the correct user.
If that passes, then add the constraint back in for validation on your users controller, and figure out how to test your sign_in method. :)
Ok so i figured it out from debugging through the current_account not getting set. I found this answer Rails rspec set subdomain.
I guess i left out a major piece of the puzzle in my original question so i apologize about that as I had no idea that would affect my rspecs.
The trick was to
before(:each) do
#account = FactoryGirl.create(:account)
#request.host = "#{#account.subdomain}.example.com"
end
Can't figure out why factory girl isn't generating my Question id's for me.
Factories:
users:
require 'faker'
FactoryGirl.define do
factory :user do
email {Faker::Internet.email}
name {Faker::Name.name}
password '12345678'
password_confirmation '12345678'
end
end
questions:
require 'faker'
FactoryGirl.define do
factory :question do
association :user
title "Random question about Ruby on Rails"
body { Faker::Lorem.paragraph(2) }
end
end
Feature test
require 'spec_helper'
feature "Show questions" do
given(:user) { FactoryGirl.create(:user) }
given(:question) { FactoryGirl.build(:question, user_id: user.id) }
scenario 'guest user cannot visit edit question path' do
visit edit_question_path(question)
expect(current_url).to eq root_url
end
...
end
Terminal fail:
ActionController::UrlGenerationError:
No route matches {:action=>"edit", :controller=>"questions", :format=>nil,
:id=>#<Question id: nil, title: "Random question about Ruby on Rails",
body: "Dolor in doloribus labore. Totam debitis eveniet v...", created_at: nil, updated_at: nil, user_id: 6309>} missing required keys: [:id]
I know the solution is out there, but I can't understand the problem. Thank you!
Using build creates an in memory object which has no database id yet
FactoryGirl.build(:question, user_id: user.id)
Using create will create the record in the database with an id
FactoryGirl.create(:question, user_id: user.id)
Your current code is trying to go the edit page for a Question that has never been saved
I'm doing exactly what is says in the documentation, but still getting a validation error:
Validation failed: Email has already been taken
FactoryGirl.define do
sequence :email do |n|
"test#{n}#factory.com"
end
factory :user do
email
password '12345678'
password_confirmation '12345678'
goal_id 1
experience_level_id 1
gender 'Female'
end
end
Anyone know what I'm doing wrong here?
EDIT:
Here is the failing spec. It works fine if you uncomment subject block and comment out the FactoryGirl stuff. I'm trying to switch to using FactoryGirl.
require 'spec_helper'
require 'cancan/matchers'
describe User do
# subject(:user) do
# Program.create!(name: 'test', gender: 'Female', goal_id: '1', experience_id: '1')
# User.create!(email: 'test#test.com', password: '12345678', password_confirmation: '12345678', goal_id: '1', experience_level_id: '1', gender: 'Female')
# end
FactoryGirl.create(:program)
puts FactoryGirl.create(:user).inspect
it "should be assigned a program when it's created" do
user.programs.should exist
end
it "should be valid with a name, goal, password, password_confirmation, experience_level, and gender" do
user.should be_valid
end
it { should respond_to(:programs) }
its('programs.last.name') {should == 'Test'}
it "should be assigned imperial as the default measurement_units" do
user.measurement_units.should eq("imperial")
end
it 'validates presence of gender, goal_id, and experience_level_id' do
user = User.new(gender: nil)
user.should validate_presence_of(:gender)
user.should validate_presence_of(:goal_id)
user.should validate_presence_of(:experience_level_id)
end
end
Edit 2:
I've updated my spec following the suggestion of one of the answers, so now my spec runs, but I get a failing test with a validation error. Here is the updated code:
describe User do
subject(:user) do
# Program.create!(name: 'test', gender: 'Female', goal_id: '1', experience_id: '1')
# User.create!(email: 'test#test.com', password: '12345678', password_confirmation: '12345678', goal_id: '1', experience_level_id: '1', gender: 'Female')
FactoryGirl.create(:program)
FactoryGirl.create(:user)
end
it "should be assigned a program when it's created" do
user.programs.should exist
end
And the message from the failing test:
Failures:
1) User should be assigned a program when it's created
Failure/Error: FactoryGirl.create(:user)
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
# ./spec/models/user_spec.rb:9:in `block (2 levels) in <top (required)>'
# ./spec/models/user_spec.rb:13:in `block (2 levels) in <top (required)>'
Any ideas?
You can't just create Factory instances in the middle of a test class like that. FactoryGirl is a replacement for the instantiation you had in the code before - leave the subject block, and simply replace the two create lines with the FactoryGirl calls.
(even better, they should be let blocks not a subject block, but that's a different story)
If you are not doing things in a context Rspec knows about, it has no way of cleaning up afterwards.
What do you have in your Gemfile? I have better success using this
gem 'factory_girl_rails', :group => :test
and not listing it in the "group :development, :test do" section.
I have a User model which has methods for a next and previous user:
def next_user
User.where("id > ?", id).order("id ASC").first
end
def prev_user
User.where("id < ?", id).order("id DESC").first
end
I'm trying to setup a test using RSpec and Factory girl to test this, and am not sure of the approach - i'm only getting started with TDD.
I'm trying this:
describe "previous_next" do
let(:user) { FactoryGirl.create(:user) }
let(:next_user) { FactoryGirl.create(:user) }
it "should know the next user" do
expect(user.next_user).to eq next_user
end
it "should know the previous user" do
expect(next_user.prev_user).to eq user
end
end
here is the error
1) User previous_next should know the next user
Failure/Error: expect(user.next_user).to eq next_user
expected: #<User id: 7624, name: "Example User", email: "sample9#email.com", created_at: "2013-01-13 04:31:19", updated_at: "2013-01-13 04:31:19", password_digest: "$2a$04$mbzI.yXYd9eSSfXbjChROewwvCHLFQI6qq5IUNzAKo9O...", remember_token: "KMSmiEeOr6f_Sgi8KJffIA", admin: false, plan_id: nil, password_reset_token: nil, password_reset_sent_at: nil, stripe_customer_token: nil>
got: nil
(compared using ==)
# ./spec/models/user_spec.rb:93:in `block (3 levels) in <top (required)>'
2) User previous_next should know the previous user
Failure/Error: expect(next_user.prev_user).to eq user
expected: #<User id: 7626, name: "Example User", email: "sample11#email.com", created_at: "2013-01-13 04:31:19", updated_at: "2013-01-13 04:31:19", password_digest: "$2a$04$uzrKmsXmVY7jUdRtfnfhUe89Jgy1176zE2UxdH90imJR...", remember_token: "gl10QvxjSMZYQS3JIgbREg", admin: false, plan_id: nil, password_reset_token: nil, password_reset_sent_at: nil, stripe_customer_token: nil>
got: nil
(compared using ==)
# ./spec/models/user_spec.rb:97:in `block (3 levels) in <top (required)>'
If i'm creating the next_user record as an instance of the :user Factory, why is it evaulating to nil?
The problem is that let lazily evaluates, it doesn't create the records until you ask for them.
Use let! instead of let. That eagerly evaluates them and forces the records to be created before the test.