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
Related
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
I have two models Users and Accounts which has the relationship
Users has_many Accounts via `created_by_id`
users.rb
FactoryGirl.define do
factory :user do
first_name {Faker::Name.first_name}
last_name {Faker::Name.last_name}
email {Faker::Internet.email}
username {Faker::Internet.user_name}
password {Faker::Internet.password}
end
end
accounts.rb
FactoryGirl.define do
factory :account do
name {Faker::Company.name}
association :user
end
end
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, :confirmable, :invitable
has_many :accounts
# validations
validates :first_name, presence: true
validates :last_name, presence: true
validates :email, presence: true,
uniqueness: {case_sensitive: false},
:email => true
validates :username, uniqueness: {case_sensitive: false, allow_blank: true}
before_validation :downcase_attributes
def name
"#{first_name} #{last_name}"
end
private
def downcase_attributes
self.email = email.try(:downcase)
self.username = username.try(:downcase)
end
end
Accounts Model
class Account < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :slugged
belongs_to :users, foreign_key: "created_by_id"
# Methods
def should_generate_new_friendly_id?
name_changed?
end
end
When i try to Testing the Validity of the Factories by the following
require 'rails_helper'
describe Account, type: :model do
context "valid Factory" do
it "has a valid factory" do
expect(build(:account)).to be_valid
end
end
context "validations" do
before { create(:account) }
context "presence" do
it { should validate_presence_of :name }
end
end
end
Error
Account
valid Factory
has a valid factory (FAILED - 1)
validations
presence
example at ./spec/models/account_spec.rb:15 (FAILED - 2)
Failures:
1) Account valid Factory has a valid factory
Failure/Error: expect(build(:account)).to be_valid
NoMethodError:
undefined method `user=' for #<Account:0x007f858a4783a0>
# ./spec/models/account_spec.rb:7:in `block (3 levels) in <top (required)>'
2) Account validations presence
Failure/Error: before { create(:account) }
NoMethodError:
undefined method `user=' for #<Account:0x007f8595154088>
# ./spec/models/account_spec.rb:12:in `block (3 levels) in <top (required)>'
Finished in 0.87731 seconds (files took 5.61 seconds to load)
2 examples, 2 failures
Failed examples:
rspec ./spec/models/account_spec.rb:6 # Account valid Factory has a valid factory
rspec ./spec/models/account_spec.rb:15 # Account validations presence
This line:
belongs_to :users, foreign_key: "created_by_id"
should be changed to:
belongs_to :user, foreign_key: :created_by_id # notice singular :user
From docs:
A belongs_to association sets up a
one-to-one connection with another model, such that each instance of
the declaring model "belongs to" one instance of the other model. ...
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.
UPDATE POST I'm noob in RoR and i start the test. I have an application and I try to use test with rspec and capybara, I want create user and test the login. But when i do my test i have some error with my models users, because in my app I create user and i call an after_create :create_order
I modify my factories.rb but i have an error in my update_attributes
See my model user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
has_many :ratings
has_many :rated_sounds, :through => :ratings, :source => :sounds
has_many :sounds ,:dependent => :destroy
has_many :orders ,:dependent => :destroy
has_many :song_ups ,:dependent => :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:registerable, :confirmable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me,
:nom, :prenom, :societe, :tel, :cgu, :sign_in_count, :trans_simu,
:trans_limit, :project, :vat_number, :adress, :zip_code, :city, :tutorial
after_create :create_order
def create_order
order = Order.create(user_id: self.id,files_left: 3)
order.subscription = Subscription.where(category: 'test').first || Subscription.create(category: 'test')
self.update_attribute(:trans_limit, 1)
#Ancien Order
# Order.create(user_id: self.id, subscription_id: Subscription.where(category: 'test').first.id, files_left: 3)
# self.update_attribute(:trans_limit, 1)
end
def test?
self.orders.trial.present? && self.orders.count==1
end
def unlimited?
!self.test? && self.orders.current.where(time_left: -1).any?
end
def allow_send?
!self.finish_order? && self.sounds.in_progress.count < self.trans_limit.to_i
end
def finish_order?
self.orders.current.empty?
end
end
For create my user in my test I use FactoryGirl. And i write this :
require 'factory_girl'
FactoryGirl.define do
factory :user do
sequence(:email) {|n| "email#{n}#factory.com" }
password "foobar"
password_confirmation "foobar"
societe "RspecTest"
prenom "John"
nom "Doe"
tel "0101010101"
confirmed_at Time.now
association :order
end
factory :order do
association :subscription
end
factory :subscription do
end
end
And one of my test is :
scenario "User login right" do
visit new_user_session_path
current_path.should == "/users/sign_in"
page.html.should include('<h2>Se connecter</h2>')
user = FactoryGirl.create(:user)
fill_in "Email", :with => user.email
fill_in "Mot de passe", :with => user.password
check "user_remember_me"
click_button "Connexion"
page.should have_content('Mon compte')
current_path.should == root_path
end
My order.rb
class Order < ActiveRecord::Base
attr_accessible :nb_files, :user_id, :period, :time_left, :subscription_id, :promo_id, :promo_end_date, :max_time_file, :files_left, :ended_at
belongs_to :user
belongs_to :subscription
scope :current, where('files_left != ? AND time_left != ? AND (ended_at >= ? OR ended_at IS ?)', 0, 0, Time.now, nil)
before_create :init_value
def self.trial
self.where(subscription_id: Subscription.where(category: 'test').first.id).first
end
def init_value
self.time_left = self.subscription.trans_seconds
self.max_time_file = self.subscription.max_time_file
if self.subscription.category != 'test'
self.user.update_attribute(:trans_limit, 1)
Order.where(user_id: self.user_id, subscription_id: Subscription.where(category: 'test')).destroy_all
else
self.files_left = 3
end
end
end
My error :
Failure/Error: user = FactoryGirl.create(:user)
NoMethodError:
undefined method `trans_seconds' for nil:NilClass
# ./app/models/order.rb:13:in `init_value'
# ./app/models/user.rb:21:in `create_order'
I hope you can help me. Thank's
You don't have Subscription with category 'test' in your database. Solution depend on how you want to handle this kind of error.
If you expect this subscription always to be in your database, use db:seed rake task for prepopulating your db. (try googling it to find out how to do this)
If you don't want to assign any subscription if given doesn't exist try:
def create_order
order = Order.create(user_id: self.id,files_left: 3)
order.subscription = Subscription.where(category: 'test').first
self.update_attribute(:trans_limit, 1)
end
And finally, if you want to create such a subscription if it doesn't exist:
def create_order
order = Order.create(user_id: self.id,files_left: 3)
order.subscription = Subscription.find_or_create_by_category('test')
self.update_attribute(:trans_limit, 1)
end
In create_order try using this:
Order.create!(user: self, subscription: Subscription.where(category: 'test').first, files_left: 3)
Use objects instead of plain ids.
On before_* methods
To make sure that validations pass, return true at the end of these methods. In your case, add return true at the end of the init_value method.
I am getting an rspec error when I have a user try to take/follow a course. I believe there must be something wrong with my method because it returns nil in the rspec test. There is also an assignment model between the course and user models. What is wrong?
The Error
1) User taking course
Failure/Error: it { should be_taking_course(course)}
expected taking_course?(#<Course id: 1, title: "Ze Finance Course 1", description: "Description for course 1", typeof: nil, created_at: "2013-06-25 21:38:24", updated_at: "2013-06-25 21:38:24">) to return true, got nil
# ./spec/models/user_spec.rb:125:in `block (3 levels) in <top (required)>'
My User Model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
has_many :assignments, foreign_key: "user_id", dependent: :destroy
has_many :courses, through: :assignments
def taking_course?(course)
assignments.find_by_course_id(course.id)
end
def take_course!(course)
assignments.create!(course_id: course.id)
end
end
User spec
require 'spec_helper'
describe User do
before(:each) do
#attr = {
:name => "Example User",
:email => "user#example.com",
:password => "changeme",
:password_confirmation => "changeme"
}
end
describe "taking course" do
let(:course) {FactoryGirl.create(:course)}
before do
#user = User.new(#attr)
#user.save
#user.take_course!(course)
end
it { should be_taking_course(course)} //this is the problem test
end
end
it { should ... } is shorthand it { subject.should ... } but you are not setting up an #user rather than the subject. So RSpec will create an implicit subject which is just User.new.
You can probably fix your test by setting an explicit subject in much the same way you set up #user:
describe "taking course" do
let(:course) {FactoryGirl.create(:course)}
subject { User.create(#attr) }
before do
subject.take_course!(course)
end
it { should be_taking_course(course) }
end
If you don't want to do that then make your assertion more explicit:
it { #user.should be_taking_course(course) }