So I have a User that has_many :comments.
In my Comments#Index, I have this:
def index
#comments = current_user.comments
end
Inside my Rspec.config... block in rails_helper.rb I have this:
# Add Config info for Devise
config.include Devise::TestHelpers, type: :controller
My comments_controller_spec.rb looks like this:
describe 'GET #index' do
it "populates an array of comments that belong to a user" do
user = create(:user)
node = create(:node)
comment1 = create(:comment, node: node, user: user)
comment2 = create(:comment, node: node, user: user)
get :index, { node_id: node }
expect(assigns(:comments)).to match_array([comment1, comment2])
end
it "renders the :index template"
end
This is my Users.rb 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
This is my Comments factory:
FactoryGirl.define do
factory :comment do
association :node
message { Faker::Lorem.sentence }
factory :invalid_comment do
message nil
end
end
end
This is the error I am getting now:
Failure/Error: get :index, { node_id: node }
NoMethodError:
undefined method `comments' for nil:NilClass
Thoughts?
You need to sign in first:
describe 'GET #index' do
let(:user) { create(:user) }
let(:node) { create(:node) }
let(:comment1) { create(:comment, node: node, user: user) }
let(:comment2) { create(:comment, node: node, user: user) }
before do
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in user
end
it "populates an array of comments that belong to a user" do
get :index, { node_id: node }
expect(assigns(:comments)).to match_array [comment1, comment2]
end
end
You could also create a module in your spec/support directory with the following code:
module SpecAuthentication
def login_user
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = FactoryGirl.create :user
sign_in #user
end
end
and include it in your RSpec.configure block:
config.include SpecAuthentication
Now you can call the login_user method in your specs:
describe 'GET #index' do
let(:node) { create(:node) }
let(:comment1) { create(:comment, node: node, user: #user) }
let(:comment2) { create(:comment, node: node, user: #user) }
before { login_user }
it "populates an array of comments that belong to a user" do
get :index, { node_id: node }
expect(assigns(:comments)).to match_array [comment1, comment2]
end
end
Update
Instead of including the module in the configure block in your spec/rails_helper.rb file, you could also add a configure block in the support file (spec/support/devise.rb) itself:
module SpecAuthorization
...
end
RSpec.configure do |config|
config.include SpecAuthorization
end
Related
def is_doctor
user = User.find_by(role: 'doctor')
"Name: #{user.name} | Email: #{user.email}| isActive: #{user.is_active}"
end
Something like this, but I don't know how to implement it correctly ↓
context 'test' do
#it { expect(user.is_doctor).to eq("Taras") }
end
I assume doctor? is an instance method on a User model + you're using Faker which might help you a lot.
# models/user.rb
class User < ApplicationRecord
def doctor?
return 'Not a doc' unless role == 'doctor'
"Name: #{name} | Email: #{email}| isActive: #{is_active}"
end
end
# specs/models/user_spec.rb
describe User, type: :model do
context 'with instance method' do
describe '#doctor?' do
subject { user. doctor? }
context 'with a doctor' do
let(:user) { create(:user, role: 'doctor') }
it 'includes name' do
expect(subject).to include("Name: #{user.name}")
end
it 'includes email' do
expect(subject).to include("Email: #{email}")
end
it 'includes is_active' do
expect(subject).to include("isActive: #{is_active}")
end
end
context 'without doctor' do
let(:user) { create(:user, role: 'foo') }
it 'has static response' do
expect(subject).to eq('Not a doc')
end
end
end
end
end
I have the following test. There are three it blocks. The first one doesn't use shoulda unlike the other two.
If I don't use the before block with post :create, product: attrs then the first test fails as expected. But If I put the before block there then the first test fails, but the other two pass. I have a uniqueness validation on product name, but that shouldn't be the problem as I'm using sequence with factory.
What should I do? How should I generally setup the data for testing when there are rspec and shoulda matchers present at the same time?
describe "when user logged in" do
before(:each) do
login_user #logged in user is available by calling #user
end
context "POST create" do
context "with valid attributes" do
let!(:profile) { create(:profile, user: #user) }
let!(:industry) { create(:industry) }
let!(:attrs) { attributes_for(:product, user_id: #user.id, industry_ids: [ industry.id ]).merge(
product_features_attributes: [attributes_for(:product_feature)],
product_competitions_attributes: [attributes_for(:product_competition)],
product_usecases_attributes: [attributes_for(:product_usecase)]
) }
it "saves the new product in the db" do
expect{ post :create, product: attrs }.to change{ Product.count }.by(1)
end
#If I don't use this the 2 tests below fail. If I use it, then the test above fails.
# before do
# post :create, product: attrs
# end
it { is_expected.to redirect_to product_path(Product.last) }
it { is_expected.to set_flash.to('Product got created!') }
end
end
end
factories
factory :product, class: Product do
#name { Faker::Commerce.product_name }
sequence(:name) { |n| "ABC_#{n}" }
company { Faker::Company.name }
website { 'https://example.com' }
oneliner { Faker::Lorem.sentence }
description { Faker::Lorem.paragraph }
user
end
You can't have it both ways. If you execute the method you are testing in the before, then you can't execute it again to see if it changes the Product count. If you don't execute it in your before, then you must execute it in your example and therefore can't use the is_expected one liner format.
There are a variety of alternatives. Here is one that incorporates the execution of the method into all the examples.
describe "when user logged in" do
before(:each) do
login_user #logged in user is available by calling #user
end
describe "POST create" do
subject(:create) { post :create, product: attrs }
context "with valid attributes" do
let!(:profile) { create(:profile, user: #user) }
let!(:industry) { create(:industry) }
let!(:attrs) { attributes_for(:product, user_id: #user.id, industry_ids: [ industry.id ]).merge(
product_features_attributes: [attributes_for(:product_feature)],
product_competitions_attributes: [attributes_for(:product_competition)],
product_usecases_attributes: [attributes_for(:product_usecase)]
) }
it "saves the new product in the db" do
expect{ create }.to change{ Product.count }.by(1)
end
it("redirects") { expect(create).to redirect_to product_path(Product.last) }
it("flashes") { expect(create).to set_flash.to('Product got created!') }
end
end
end
I have User (which is I used Devise) and Profile model where User has_one Profile as their relationship. I got an error when run the rspec test. Below is my spec to handle when user is updating his/her profile.
spec/controller/profiles_controller_spec.rb
RSpec.describe ProfilesController, type: :controller do
let(:profile) { FactoryGirl.create(:profile) }
let (:valid_attributes) { FactoryGirl.attributes_for(:profile) }
let (:invalid_attributes) { FactoryGirl.attributes_for(:profile).merge({fname: nil}) }
let(:user) { FactoryGirl.create(:user) }
let(:valid_session) { sign_in(user) }
describe "PUT #update" do
before { valid_session }
context "with valid attributes" do
it "saves valid profile" do
expect do
put :update, { id: profile.id, profile: { fname: "Cena" } }
end.to change{ profile.reload.fname }.to("Cena")
end
end
spec/factories/profiles.rb
FactoryGirl.define do
factory :profile do
user
fname "John"
lname "Doe"
avatar "my_avatar"
end
end
app/controller/profiles_controller.rb
class ProfilesController < ApplicationController
private
def user_params
params.require(:user).permit(:id, :login, :email,
profile_attributes: [
:id, :user_id, :fname, :lname, :avatar, :avatar_cache
])
end
end
And here is the error when run rspec spec/controllers/accounts_controller_spec.rb
Failures:
1) AccountsController PUT #update with valid attributes saves valid profile
Failure/Error: put :update, {id: profile.id, user_id: user.id, profile: { fname: "Cena" }}
ActionController::ParameterMissing:
param is missing or the value is empty: user
let(:user) { FactoryGirl.create(:user) }
let(:profile) { FactoryGirl.create(:profile, user_id: user.id ) }
describe "PUT #update" do
before { valid_session }
context "with valid attributes" do
it "saves valid profile" do
expect do
put :update, id: user.id, user: { profile_attributes: { user_id: user.id, fname: "Cena" } }
end.to change{ profile.reload.fname }.to("Cena")
end
end
end
The profiles_controller.rb code you posted is missing the update action (and also the class name is AccountController), but I guess you are doing something like user.update(user_params).
If that's the case, as the error says, the params passed from the controller spec does not have :user key, and that is causing the error.
Assuming from the #user_params method and the error, the params passed to the post in controller spec needs to look like the following:
{
user: {
id: xxx, ...,
profile_attributes: {
id: xxx,
fname: xxx, ...
}
}
}
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'}
Here is the method I am testing:
class User < ActiveRecord::Base
has_many :sports, :through => :user_sports, order: "user_sports.created_at", class_name: "Sport"
has_many :user_sports
def primary_sport
return nil if user_sports.blank?
user_sports.primary_only.first.sport
end
end
User Factory;
FactoryGirl.define do
sequence(:email) do |n|
"user#{n}#example.com"
end
factory :user do
email
first_name Faker::Name.first_name
last_name Faker::Name.last_name
password "password"
password_confirmation "password"
agreed_to_age_requirements true
username "testing123"
state "AL"
city_id 201
school_id 20935
handedness "Left"
customer_id { "#{rand(1000)}" }
sports {[create(:sport)]}
after(:create) do |user, elevator|
user.subscriptions << create(:subscription)
user.roles << create(:role)
end
end
factory :athlete, class: "Athlete", parent: :user do
type "Athlete"
recruit_year "2016"
end
end
Here is my test:
require 'spec_helper'
describe User do
describe "associations" do
it { should have_and_belong_to_many(:roles) }
it { should belong_to(:account_type) }
it { should belong_to(:primary_sport).class_name("Sport") }
it { should belong_to(:school) }
it { should belong_to(:city) }
it { should belong_to(:hometown) }
it { should have_many(:social_actions) }
it { should have_one(:invitation) }
it { should have_many(:authorizations) }
it { should belong_to(:user_type) }
it { should have_and_belong_to_many(:positions).class_name "SportPosition" }
it { should have_many(:sports).through(:user_sports) }
it { should have_many(:user_sports) }
it { should have_many :contributorships }
it { should have_many(:managed_athletes).through(:contributorships) }
it { should have_and_belong_to_many(:subscriptions) }
end
describe "nested attributes" do
it { should accept_nested_attributes_for(:user_sports) }
it { should accept_nested_attributes_for(:subscriptions) }
end
describe "validations" do
it { should validate_presence_of(:email) }
it { should validate_uniqueness_of(:email) }
it { should allow_value("test#test.com").for(:email) }
it { should_not allow_value("test.com").for(:email) }
end
describe "instance methods" do
before :each do
#user = create(:user, sports: [])
#school_admin_role = create(:role, name: "School Admin")
#contributor_role = create(:role, name: "Contributor")
end
describe "#my_athletes_path" do
it "returns a school admin path if the user has the role of School Admin" do
#user.roles << #school_admin_role
#user.my_athletes_path.should eq school_admin_athletes_path
end
it "returns a school admin path if the user has the role of Contributor" do
#user.roles << #contributor_role
#user.my_athletes_path.should eq contributor_dashboard_path
end
it "returns nil if the user has no Contributor or School Admin role" do
#user.my_athletes_path.should be_nil
end
end
describe "#first_time_login?" do
it "will evalute true if the user has logged in only once" do
#user.sign_in_count = 1
#user.save
#user.first_time_login?.should be_true
end
end
describe "#confirmation_required?" do
it "returns false" do
#user.confirmation_required?.should be_false
end
end
describe "#primary_sport", focus: true do
context "when user has no primary sport" do
it "returns nil" do
#user.primary_sport.should be_nil
end
end
context "when user has a primary sport" do
it "returns sport object" do
#user.sports << create(:sport)
#user.primary_sport.should eq #user.sports.first
end
end
end
end
end
This is the error I am receiving:
Failure/Error: #user.primary_sport.should eq #user.sports.first
NoMethodError:
undefined method sport for nil:NilClass
This is because when the user_sport association is created in the User Factory, the primary column is being set to false. Not sure how to fix this. Any help is greatly appreciated! Also, sorry for the ignorance on the TDD front, Im a newb
Couldn't you just add the following to your after(:create) block in the User factory:
us = user.user_sports.first
us.primary = true
us.save
That would ensure the association gets the primary flag.