In our Rails app, we have the following models:
class User < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :calendars, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Calendar < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :users, through: :administrations
end
We tried to validate the Administration model with the following administration_test.rb test file:
require 'test_helper'
class AdministrationTest < ActiveSupport::TestCase
def setup
#user = users(:noemie)
#administration = Administration.new(user_id: #user.id, calendar_id: #calendar_id)
end
test "should be valid" do
assert #administration.valid?
end
test "user id should be present" do
#administration.user_id = nil
assert_not #administration.valid?
end
test "calendar id should be present" do
#administration.calendar_id = nil
assert_not #administration.valid?
end
end
When we run the test, we get the following results:
FAIL["test_calendar_id_should_be_present", AdministrationTest, 2015-06-30 07:24:58 -0700]
test_calendar_id_should_be_present#AdministrationTest (1435674298.26s)
Expected true to be nil or false
test/models/administration_test.rb:21:in `block in <class:AdministrationTest>'
FAIL["test_user_id_should_be_present", AdministrationTest, 2015-06-30 07:24:58 -0700]
test_user_id_should_be_present#AdministrationTest (1435674298.27s)
Expected true to be nil or false
test/models/administration_test.rb:16:in `block in <class:AdministrationTest>'
We are kind of lost: is this the right way to right the test?
If no, how should we write it?
If yes, how can we make it pass?
The problem is not your test but rather that you are expecting the wrong outcome.
belongs_toin ActiveRecord does not add a validation, the macro simply creates a relation.
To validate a relation you would use validates_associated which calls #valid? on each of the associated records and validates_presence_of to ensure that the associated record is present.
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
validates_associated :user
validates :user, presence: true
end
When testing validations it is better to write on the assertions on the errors hash, as assert_not #administration.valid? will give a false positive if the validation fails for any other reason.
Bad:
test "user id should be present" do
#administration.user_id = nil
assert_not #administration.valid?
end
Good:
test "user id should be present" do
#administration.user_id = nil
#administration.valid?
assert #administration.errors.key?(:user)
end
Ok, we found the solution.
We updated our Administration model as follows:
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
validates :user_id, presence: true
validates :calendar_id, presence: true
end
And also edited our administration_test.rb file:
require 'test_helper'
class AdministrationTest < ActiveSupport::TestCase
def setup
#user = users(:noemie)
#calendar = calendars(:one)
# This code is not idiomatically correct.
#administration = Administration.new(user_id: #user.id, calendar_id: #calendar.id)
end
test "should be valid" do
assert #administration.valid?
end
test "user id should be present" do
#administration.user_id = nil
assert_not #administration.valid?
end
test "calendar id should be present" do
#administration.calendar_id = nil
assert_not #administration.valid?
end
end
The tests are now passing just fine.
Related
I am starting to get into testing with minitest in Rails. Currently my first test to see if my model is valid is returning false. The error message itself is also very generic Expected false to be truthy. I have tested everything else and all of those tests work fine. Does anybody know what could be causing this?
article_test.rb
require 'test_helper'
class ArticleTest < ActiveSupport::TestCase
def setup
#article = Article.new(title:"Avengers Endgame", body:"I am inevitable")
end
test "article should be valid" do
assert #article.valid?
end
test "title should be present" do
#article.title = " "
assert_not #article.valid?
end
test "body should not be too short" do
#article.body = "aa"
assert_not #article.valid?
end
end
article.rb
class Article < ApplicationRecord
include Visible
belongs_to :user
has_many :comments, dependent: :destroy
has_rich_text :body
validates :title, presence: true
validates :body, presence: true, length: { minimum: 10 }
end
The main problem here is that you're using a poor method for testing your validations.
assert #article.valid? and assert_not #article.valid? does not actually tell you anything at all about the object under test. When the test fails you're none the wiser about why it actually failed and if the failure is actually even connected to what you're proportedly testing. At best it serves as a sort of litmus test that your test setup is correct.
Instead of this "carpet bombing" approach test each validation on its own:
class ArticleTest < ActiveSupport::TestCas
test "title should be present" do
article = Article.new(title: '')
article.valid?
assert_includes build_article.errors[:title], "can’t be blank"
end
test "body should not be too short" do
article = Article.new(body: 'aa')
article.valid?
assert_includes article.errors[:body], "is too short"
end
end
Testing all the validations at once (creating a record with valid input) will be covered by your integration and system tests anyways.
You have belongs_to :user, which expects the #article to have a user_id compulsorily to be present, before it can be saved.
If user_id is optional during creation, change this:
belongs_to :user
to
belongs_to :user, optional: true
I have a model called Option.
class Option < ApplicationRecord
belongs_to :user
belongs_to :company
belongs_to :scheme
validate :check_for_quantity
def check_for_quantity
if self.quantity > self.scheme.remaining_options
errors.add(:quantity, "cannot be more than the remaining options #{ self.scheme.remaining_options.to_i}")
end
end
end
and a model called Scheme.
class Scheme < ApplicationRecord
belongs_to :share_class
belongs_to :equity_pool
belongs_to :company
has_many :options, dependent: :destroy
attr_accessor :percentage
def ownership
self.remaining_options * 100 / self.company.total_fdsc
end
def remaining_options
self.initial_size - self.options.sum(&:quantity)
end
end
My spec for Option Model looks like this
require 'rails_helper'
RSpec.describe Option, type: :model do
describe "Associations" do
subject { create (:option) }
it { is_expected.to belong_to(:scheme) }
it { is_expected.to belong_to(:vesting_schedule).optional }
it { is_expected.to belong_to(:user) }
it { is_expected.to belong_to(:company) }
end
end
When I run this spec the first example gives an error
1) Option Associations is expected to belong to scheme required: true
Failure/Error: if self.quantity > self.scheme.remaining_options
NoMethodError:
undefined method `remaining_options' for nil:NilClass
# ./app/models/option.rb:9:in `check_for_quantity'
What is the problem here?
My options factory bot
FactoryBot.define do
factory :option do
security "MyString"
board_status false
board_approval_date "2018-08-16"
grant_date "2018-08-16"
expiration_date "2018-08-16"
quantity 1
exercise_price 1.5
vesting_start_date "2018-08-16"
vesting_schedule nil
scheme
user
company
end
end
Just add a condition to the validation so that it is not fired if the association is nil.
class Option < ApplicationRecord
belongs_to :user
belongs_to :company
belongs_to :scheme
validate :check_for_quantity, unless: -> { self.scheme.nil? }
def check_for_quantity
if self.quantity > self.scheme.remaining_options
errors.add(:quantity, "cannot be more than the remaining options #{ self.scheme.remaining_options.to_i}")
end
end
end
You may also want to ensure that self.quantity is a number and not nil to avoid NoMethodError: undefined method > for nil:NilClass which you can do with a numericality validation.
class Option < ApplicationRecord
belongs_to :user
belongs_to :company
belongs_to :scheme
validates_numericality_of :quantity
validate :check_for_quantity, if: -> { self.scheme && self.quantity }
def check_for_quantity
if self.quantity > self.scheme.remaining_options
errors.add(:quantity, "cannot be more than the remaining options #{ self.scheme.remaining_options.to_i}")
end
end
end
I am having an issue regarding Factory Girl in Rails. I currently have pundit setup am trying to test my user policies yet the factory does not seem to work when in rspec.
Inside the rails console, I can load up the console and type:
user=FactoryGirl.create(:admin_user)
r=user.roles
This works correctly creates a user and the correct associations between a user and a role. However, when the factory is used in rspec- A user is created but not the associated assignment. (I discovered this by using 'pp' inside the specific tests.)
I do not need to create a role, since the roles are set so I am looking them up.
Any ideas?
Models
class User < ApplicationRecord
has_many :assignments, dependent: :destroy
has_many :roles, through: :assignments, dependent: :destroy
def has_role?(roles)
roles.each do |role|
if self.roles.include? role
return true
end
end
false
end
class Role < ApplicationRecord
# Associations
has_many :assignments
has_many :users, through: :assignments
class Assignment < ApplicationRecord
# Associations
belongs_to :user
belongs_to :role
Factories
FactoryGirl.define do
factory :user do
first_name {Faker::Name.first_name}
last_name {Faker::Name.last_name}
email {Faker::Internet.email}
password {Faker::Internet.password(8)}
factory :admin_user do
after(:create) do |user|
Assignment.create(user: user , role: Role.find_by(label:'System Admin') )
end
end
end
end
Tests
User Policy Test
describe UserPolicy do
subject { UserPolicy }
let (:current_user) { FactoryGirl.build_stubbed :user}
let (:other_user) { FactoryGirl.build_stubbed :user }
let (:admin) { FactoryGirl.build_stubbed :admin_user}
permissions :index? do
it "denies access if not an admin" do
expect(UserPolicy).not_to permit(current_user)
end
it "allows access for an admin" do
expect(UserPolicy).to permit(admin)
end
end
end
Other Test With same Issue
feature 'User index page', :devise do
after(:each) do
Warden.test_reset!
end
scenario 'user sees own email address' do
user = FactoryGirl.create(:admin_user)
expect(user.has_role?(Role.where(label: 'System Admin'))).to eq true
login_as(user, scope: :user)
visit users_path
expect(page).to have_content user.email
end
end
This test fails since the user has no role assigned.
Controller
class AssignmentsController < ApplicationController
def create
#assignment = Assignment.new(assignment_params)
if #assignment.save
redirect_to users_path(), :notice => "Role Added"
else
flash[:alert]="Unable to Add Role"
end
end
I cannot get Factory Girl to produce a User object when accepts_nested_attributes_for is enabled.
Given the models:
class Account < ApplicationRecord
belongs_to :meta, polymorphic: true, dependent: :destroy
end
class User < ApplicationRecord
has_one :account, as: :meta
validates :fname, presence: true
validates :lname, presence: true
validates_presence_of :account
#accepts_nested_attributes_for :account
end
And, the factories:
FactoryGirl.define do
factory :account do
sequence(:email) { |n| "account#{n}#mack.com" }
password "abcdef123"
password_confirmation "abcdef123"
confirmed_at Date.today
end
end
FactoryGirl.define do
factory :user do
sequence(:fname) { |n| "first#{n}" }
sequence(:lname) { |n| "last#{n}" }
after(:build) do |user|
user.build_account attributes_for(:account)
end
end
end
The following spec:
describe User, :type => :model do
it "has a valid factory" do
user = build(:user)
expect(user).to be_valid
end
end
Fails if I uncomment the accepts_nested_attributes_for in the User model. I would like to use the nested attributes for working with User objects, but I cannot figure out how to get FactorGirl to build a User with nested attributes enabled.
I get the following error:
1) User has a valid factory
Failure/Error: expect(user).to be_valid
expected #<User id: nil, fname: "first1", lname: "last1", created_at: nil, updated_at: nil> \
to be valid, but got errors: Account meta must exist
I am using Rails version 5.0.0beta1 and Factory Girl 4.6.0.
I've been battling this all day -- thanks for your help.
Edit: I see the same behavior using the rails console, so it would seem like the issue is independent of Factory Girl.
i have two error when i run rake test:
1)Error
test_should_require_a_following_id(RelationTest)
NoMethodError:underfined method 'assert_not' for #<RelationTest:0x3dd0058>
2)Error
test_should_require_a_follower_id(RelationTest)
NoMethodError:underfined method 'assert_not' for #<RelationTest:0x3c1d700>
this is my relation_test.rb
require 'test_helper'
class RelationTest < ActiveSupport::TestCase
def setup
#relation = Relation.new(follower_id: 9, following_id: 10)
end
test "should be valid" do
assert #relation.valid?
end
test "should require a follower_id" do
#relation.follower_id = nil
assert_not #relation.valid?
end
test "should require a following_id" do
#relation.following_id = nil
assert_not #relation.valid?
end
end
my relation model
class Relation < ActiveRecord::Base
attr_accessible :following_id, :follower_id
belongs_to :follower, :class_name => "User"
belongs_to :following, :class_name => "User"
validates :follower_id, presence: true
validates :following_id, presence: true
end
What can i do to fix that, please help me!
I think the problem is my rail vesion(3.2.19), but assert #relation.valid?still work??
assert_not has been added in rails 4.0.2. You need to do this instead:
assert !#relation.valid?