I've integrated Devise with my RoR app and am now trying to test my Controllers, specifically the one that routes me to my root_url.
I've used this HOWTO on Devise's page to setup my admin/user Factories, but there is an additional component that is part of my user signup process, which is creating a Company.
So:
User: has_one :company
Company: has_many :users
The flow for a new user looks like this:
User signs up
User confirms account (via email) and is redirected to the login page
User logs in
User fills out Company information and submits
User is then redirected to Pages#home (which is my root_url)
Using Devise's HOWTO, I created a ControllerHelpers file within Support:
module ControllerHelpers
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
user.confirm # or set a confirmed_at inside the factory. Only necessary if you are using the "confirmable" module
sign_in user
end
end
end
I suspect there is something wrong with my User Factory since it doesn't seem like a Company is being created, but I'm so new to RSpec, I'm unsure.
FactoryGirl.define do
factory :user do
first_name "Test"
last_name "User"
full_name "Test User"
email "test#user.com"
phone_number "111-222-3333"
terms_accepted true
time_zone "Central Time (US & Canada)"
password "password"
password_confirmation "password"
confirmed_at Date.today
association :company
end
end
And I have a company.rb factory as well:
FactoryGirl.define do
factory :company do
id 1
name "ACME Test"
address_1 "123 Shady Lane."
address_2 "Suite 400"
city "Testville"
state "Test"
zip_code "12345"
has_payment_plan false
stripe_id "cus_34d434343e4e3e3"
locked false
end
end
My pages_controller_spec.rb is simple at this point:
require 'rails_helper'
RSpec.describe PagesController, :type => :controller do
describe "User: GET #home" do
login_user
it "signs in the user" do
expect(response).to render_template(:home)
end
end
end
This results in the following RSpec error:
1) PagesController User: GET #home signs in the user
Failure/Error: expect(response).to render_template(:home)
expecting <"home"> but was a redirect to <http://test.host/companies/new>
# ./spec/controllers/pages_controller_spec.rb:10:in `block (3 levels) in <top (required)>'
So, it's not even doing the render_template portion of my test?
UPDATE: Added Home Controller
controllers/pages_controller#home
def home
if current_user && current_user.company
verify_subscription
get_company_and_locations
get_network_hosts
get_network_hosts_at_risk
#network_hosts_snip = #network_hosts_at_risk.sort_by{ |h| -h.security_percentage }.first(5)
get_company_issues
#issues = #issues.sort_by{ |i| -i.cvss_score }.first(5)
#deferred_issues = #company.deferred_issues.last(5)
#deferred_hosts = #company.deferred_hosts.last(5)
else
redirect_to new_company_path
end
end
As we found out together in the chat... Your company was created but wasn't persisted to DB. It's because you had strategy: :build in your factory.
strategy: :build means that your association object will be created but won't be persisted to DB. To persist it you should use strategy: :create. Or in your use case you can replace association :company, strategy: :build with just company. FactoryGirl is smart enough to recognize it as an association which must be created and persisted.
And you need to set up FactoryGirl association between company and subscription the same way.
Related
I have two user roles and i want to write a test in rspec to see if the user role are attached to the user after creation. Also admins should be able to switch the user roles from one to the other.
Here is my user model how i structure the type of users using a enum
enum role: [:batman, :superman]
after_initialize :set_default_role, :if => :new_record?
protected
def set_default_role
self.role ||= :batman
end
I am stuck on the test below and not sure how to go about checking the if the role was successfully attached. Also is there a way to check if the user role can be changed for a user? For example if the user was created with the role of batman, it can be switch to superman?
RSpec.describe User, type: :model do
before do
#user = FactoryBot.create(:user)
end
describe "creation" do
it "can be created" do
expect(#user).to be_valid
end
end
end
You can write expectations for model fields too. Code is pretty self-explanatory:
let(:user){ create(:user) }
it "has role batman" do
expect(user.role).to eq("batman")
end
For changing:
it "changes role" do
expect{
do_something_with(user)
}.to change{ user.reload.role }.from("batman").to("superman")
end
reload might be not needed in model tests, but usually is for other (request/system/etc) where record can change in db but not in exact instance in memory.
I am trying to write a test for my InvitationsController#Create.
This is a POST http action.
Basically what should happen is, once the post#create is first executed, the first thing that needs to do is we need to check to see if a User exists in the system for the email passed in via params[:email] on the Post request.
I am having a hard time wrapping my head around how I do this.
I will refactor later, but first I want to get the test functionality working.
This is what I have:
describe 'POST #create' do
context 'when invited user IS an existing user' do
before :each do
#users = [
attributes_for(:user),
attributes_for(:user),
attributes_for(:user)
]
end
it 'correctly finds User record of invited user' do
post :create, { email: #users.first[:email] }
expect(response).to include(#users.first[:email])
end
end
end
This is the error I get:
1) Users::InvitationsController POST #create when invited user IS an existing user correctly finds User record of invited user
Failure/Error: post :create, { email: #users.first[:email] }
NoMethodError:
undefined method `name' for nil:NilClass
##myapp/gems/devise-3.2.4/app/controllers/devise_controller.rb:22:in 'resource_name'
# #myapp/gems/devise_invitable-1.3.6/lib/devise_invitable/controllers/helpers.rb:18:in 'authenticate_inviter!'
# #myapp/gems/devise_invitable-1.3.6/app/controllers/devise/invitations_controller.rb:67:in 'current_inviter'
# #myapp/gems/devise_invitable-1.3.6/app/controllers/devise/invitations_controller.rb:71:in 'has_invitations_left?'
I am using FactoryGirl and it works perfectly, in the sense that it returns valid data for all the data-types. The issue here is how do I get RSpec to actually test for the functionality I need.
Edit 1
Added my :user factory:
FactoryGirl.define do
factory :user do
association :family_tree
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
email { Faker::Internet.email }
password "password123"
password_confirmation "password123"
bio { Faker::Lorem.paragraph }
invitation_relation { Faker::Lorem.word }
# required if the Devise Confirmable module is used
confirmed_at Time.now
gender 1
end
end
It seems you're using Devise which require you to be logged in before going to the next step. On your error, Devise cannot get the same of your inviter because he's not logged.
Your test should be like this:
describe 'POST #create' do
context 'when invited user IS an existing user' do
before :each do
#users = [
attributes_for(:user),
attributes_for(:user),
attributes_for(:user)
]
#another_user = FactoryGirl.create(:user_for_login)
sign_in #another_user
end
it 'correctly finds User record of invited user' do
post :create, { email: #users.first[:email] }
expect(response).to include(#users.first[:email])
end
end
end
Example for FactoryGirl model for Devise
factory :user_for_login, class: User do |u|
u.email 'admin#myawesomeapp.com'
u.password 'password'
u.password_confirmation 'password'
u.name "MyName"
end
Of course, you need to add as much data as your validators want.. Basically for Devise you need email, password and password_confirmation. In you case, it seems you also need name.
I'm writing basic tests for a simple CRUD rails application. I am using devise for authentication and Factory Girl for object creation whilst testing, after testing I am clearing the test.db with the Database Cleaner gem.
I am testing a controller with RSpec but need an admin user to be signed in for this to be a 'true' test.
So far I have been following documentation but I don't believe it is working.
I have a test which checks if the count has been changed by one:
describe "POST create" do
context "with valid attributes" do
it "creates a new room" do
expect{ post :create, room: FactoryGirl.attributes_for(:room) }.to change(Room,:count).by(1)
end
When I run the test suite I get the error:
expected #count to have changed by 1, but was changed by 0
From reading around it seems I need to setup authentication with my tests. To do this I have created relevant Factories:
# Devise User Class
factory :user do
email "basicuser#mvmanor.co.uk"
password "u"
password_confirmation "u"
admin false
customer false
end
# Admin
factory :admin, class: User do
email "basicadmin#mvmanor.co.uk"
password "a"
password_confirmation "a"
admin true
customer false
end
I have also created the relevant Devise mappings macros:
module UserControllerMacros
def login_admin
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:admin]
sign_in FactoryGirl.create(:admin) # Using factory girl as an example
end
end
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in FactoryGirl.create(:user) # Using factory girl as an example
end
end
end
I'm not sure if I'm heading in the right direction with this. I certainly need my controller tests to be authenticated.
Before the first describe statement add this:
let!(:admin) { FactoryGirl.create(:admin) }
before { subject.stub(current_user: admin, authenticate_user!: true) }
it should stub your authentication.
And one little trick for your happiness: add in your spec_helper.rb anywhere within
RSpec.configure do |config|
...
end
block this code:
config.include FactoryGirl::Syntax::Methods
and now you don't need to preface all factory_girl methods with FactoryGirl, so instead of FactoryGirl.create you can write just create.
I was looking at this answer to see how to test a session controller and wrote something like this:
require 'spec_helper'
describe SessionsController do
context "We should login to the system and create a session" do
let :credentials do
{:user_name => "MyString", :password => "someSimpleP{ass}"}
end
let :user do
FactoryGirl.create(:user, credentials)
end
before :each do
post :create , credentials
end
it "should create a session" do
puts user.inspect
puts session[:user_id]
#session[:user_id].should == user.id
end
end
end
Based on that I created a factory girl user:
FactoryGirl.define do
factory :user, :class => 'User' do
name "sample_user"
email "MyString#gmail.com"
user_name "MyString"
password "someSimpleP{ass}"
end
end
Now it all works - exceot for the before :each do statement - it never "logs" the "user" in - thus I cannot test the controllers functionality of, is a session properly created?
Now most would say, use capybara and test it through that way - but that's wrong, IMO - sure if I'm doing front end testing that would work, but I'm testing controller based logic. Can some one tell me why this isn't working? routing works fine.
My puts session[:user_id] is coming up nil, when it shouldn't
let is lazily evaluated, even for the before clause, so the user has not been created as of the time you do the post to login. If you change to using let!, you'll avoid this problem.
You misunderstood SessionsController and RegistrationsController.
A Session is for an user who has already registered, not for creating an user. #create in SessionController means to create a session, not an user.
RegistrationController is for creating user with full details including password_confirmation.
To test SessionsController, you need to create a valid user in FactoryGirl at first, then use his credentials say email and password to sign in.
I'm testing a controller in my app which should render one view for users who've given over their info and requested access ("signed in guests") and another view for the rest of the world (anonymous users). I'm using OmniAuth and Rolify.
Test goes like this:
context 'signed in guests' do
before do
guest = create(:user,:guest)
OmniAuth.config.mock_auth[:google] = {
uid: guest.uid
}
session[:user_id] = guest.id
end
it "shows the Please Wait for Confirmation view" do
puts "Rspec says Current User: #{#current_user.inspect}"
get :index
response.should render_template :pending_authorization
end
end
User factory looks like this:
FactoryGirl.define do
factory :user do
name "MyString"
email "MyString"
provider "MyString"
uid "MyString"
trait :admin do
after(:create) {|user| user.add_role(:admin)}
end
trait :guest do
after(:create) {|user| user.add_role(:guest)}
end
trait :authorized_user do
after(:create) {|user| user.add_role(:user)}
end
end
end
However, when I run this, my puts actually gives me this:
Rspec says Current User: nil
So... why is the current user nil even though I create it with the omniauth mock?
Edit: current_user comes from my application helper, if that helps any.
Thanks in advance!