I'm trying to test this after_initialize callback which is for the item model (which has_many line_items):
after_initialize :build_default_items, unless: :line_items?
callback:
def build_default_items
LineOfBusiness.all.each do |lob|
line_items.new(line_of_business_id: lob.id)
end
end
My test looks like:
describe 'callbacks' do
let(:user) { create :user }
it 'should build default items' do
lob1 = LineOfBusiness.create(id:1, name: "Name1", eff_date: Date.today,exp_date: Date.tomorrow, create_user: user, update_user: user)
lob2 = LineOfBusiness.create(id:2, name: "Name2", eff_date: Date.today,exp_date: Date.tomorrow, create_user: user, update_user: user)
lob_count = LineOfBusiness.all.count # this is correct as 2
item = build :item
expect(item.line_items.count).to eq(lob_count)
end
end
Error message as follows:
expected: 2
got: 0
(compared using ==)
So its failing in the callback method, its seeing the LineOfBusiness.all as Nil
def build_default_items
LineOfBusiness.all.each do |lob| # <-- this is Nil so fails
line_items.new(line_of_business_id: lob.id)
end
end
Any ideas why its Nil in the callback method?
line_items.count will fire query to database, and as you are not saving line_items in after_initialize callback, spec will fail. Instead try using line_items.size.
expect(item.line_items.size).to eq(lob_count)
Related
I'm trying to test if a specific attribute some_field gets assigned with correct values. I'm currently not persisting the records on the DB due to speed issues so I need a way thru stubs only.
Here's a sample method:
class Teacher < ApplicationRecord
has_many :students
def my_method
...
teacher.students.find_each do |s|
s.some_field[:foo] = 'bar'
s.save
end
end
end
Here's the test spec which is failing since :find_each only works with ActiveRecord Relations:
it 'assigns a correct value in some_field attribute' do
allow(teacher).to receive(:students).and_return([student1])
allow(student1).to receive(:save)
teacher.my_method
expect(student1.some_field).to eq({ foo: 'bar' })
end
Error:
NoMethodError:
undefined method `find_each' for #<Array:0x00007fa08a94b308>
I'm wondering if there's a way for this without persisting in DB?
Any help will be appreciated.
Also mock the find_each and return regular each instead.
let(:teacher) { double }
let(:students_mock) { double }
let(:student1) { double }
it do
expect(teacher).to receive(:students).and_return(students_mock)
expect(students_mock).to receive(:find_each) { |&block| [student1].each(&block) }
expect(student1).to receive(:save)
teacher.students.find_each(&:save)
end
I want to test #create in Decidingscontroller , and Deciding model has undertaking_id column in association with Undertaking model , and has asking_id column in association with Asking model.
So, My factories/decidings.rb is below.
factory :deciding do
after(:build) do |deciding|
deciding.asking ||=build(:asking, deciding: deciding)
deciding.undertaking ||=build(:undertaking, deciding: deciding)
end
end
and My spec/controllers/decidings_controller_spec.rb is below.
RSpec.describe DecidingsController, type: :controller do
describe '#create' do
before do
#deciding=build(:deciding)
end
context 'correct_user login' do
before do
login_user(#deciding.asking.user)
end
it 'creates with deciding +1' do
expect{post :create , undertaking_id: #deciding.undertaking_id , asking_id: #deciding.asking_id}.to change(Deciding , :count).by(1)
end
end
end
end
but #deciding in this case is below.
#<Deciding id: nil, asking_id: nil, undertaking_id: nil, created_at: nil, updated_at: nil>
so I can't create test because undertaking_id and asking_id is nil.
Why is undertaking_id and asking_id nil? Please help me...
Anyway , My factories/asking.rb is below.
FactoryGirl.define do
factory :asking do
association :user
sequence(:content){|i| "お願いします#{i}"}
end
end
The ids are nil because the records are not persisted. When you use build method, it does not save a record in the database. If a record is not saved, it cannot have ID. Use create method instead.
Try this:
factory :deciding do
after(:build) do |deciding|
deciding.asking ||= create(:asking)
deciding.undertaking ||= create(:undertaking)
end
end
I try to test validation method that check times overlap for activities.
There are three factories(two of them inherit from activity).
Factories:
activities.rb
FactoryGirl.define do
factory :activity do
name 'Fit Girls'
description { Faker::Lorem.sentence(3, true, 4) }
active true
day_of_week 'Thusday'
start_on '12:00'
end_on '13:00'
pool_zone 'B'
max_people { Faker::Number.number(2) }
association :person, factory: :trainer
factory :first do
name 'Swim Cycle'
description 'Activity with water bicycles.'
active true
day_of_week 'Thusday'
start_on '11:30'
end_on '12:30'
end
factory :second do
name 'Aqua Crossfit'
description 'Water crossfit for evereyone.'
active true
day_of_week 'Thusday'
start_on '12:40'
end_on '13:40'
pool_zone 'C'
max_people '30'
end
end
end
Activities overlaps when are on same day_of_week(activity.day_of_week == first.day_of_week), on same pool_zone(activity.pool_zone == first.pool_zone) and times overlaps.
Validation method:
def not_overlapping_activity
overlapping_activity = Activity.where(day_of_week: day_of_week)
.where(pool_zone: pool_zone)
activities = Activity.where(id: id)
if activities.blank?
overlapping_activity.each do |oa|
if (start_on...end_on).overlaps?(oa.start_on...oa.end_on)
errors.add(:base, "In this time and pool_zone is another activity.")
end
end
else
overlapping_activity.where('id != :id', id: id).each do |oa|
if (start_on...end_on).overlaps?(oa.start_on...oa.end_on)
errors.add(:base, "In this time and pool_zone is another activity.")
end
end
end
end
I wrote rspec test, but unfortunatelly invalid checks.
describe Activity, 'methods' do
subject { Activity }
describe '#not_overlapping_activity' do
let(:activity) { create(:activity) }
let(:first) { create(:first) }
it 'should have a valid factory' do
expect(create(:activity).errors).to be_empty
end
it 'should have a valid factory' do
expect(create(:first).errors).to be_empty
end
context 'when day_of_week, pool_zone are same and times overlap' do
it 'raises an error that times overlap' do
expect(activity.valid?).to be_truthy
expect(first.valid?).to be_falsey
expect(first.errors[:base].size).to eq 1
end
end
end
end
Return:
Failure/Error: expect(first.valid?).to be_falsey
expected: falsey value
got: true
I can't understand why it got true. First create(:activity) should be right, but next shouldn't be executed(overlapping).
I tried add expect(activity.valid?).to be truthy before expect(first.valid?..., but throws another error ActiveRecord::RecordInvalid. Could someone repair my test? I'm newbie with creation tests using RSpec.
UPDATE:
Solution for my problem is not create :first in test but build.
let(:first) { build(:first) }
This line on its own
let(:activity) { create(:activity) }
doesn't create an activity. It only creates an activity, when activity is actually called. Therefore you must call activity somewhere before running your test.
There are several ways to do so, for example a before block:
before { activity }
or you could use let! instead of just let.
I'm currently trying to write an RSpec test for a validation method. This method is triggered when the record is updated, saved or created. Here is what I have so far:
product.rb (model)
class Product < ActiveRecord::Base
validate :single_product
# Detects if a product has more than one SKU when attempting to set the single product field as true
# The sku association needs to map an attribute block in order to count the number of records successfully
# The standard self.skus.count is performed using the record ID, which none of the SKUs currently have
#
# #return [boolean]
def single_product
if self.single && self.skus.map { |s| s.active }.count > 1
errors.add(:single, " product cannot be set if the product has more than one SKU.")
return false
end
end
end
products.rb (FactoryGirl test data)
FactoryGirl.define do
factory :product do
sequence(:name) { |n| "#{Faker::Lorem.word}#{Faker::Lorem.characters(8)}#{n}" }
meta_description { Faker::Lorem.characters(10) }
short_description { Faker::Lorem.characters(15) }
description { Faker::Lorem.characters(20) }
sku { Faker::Lorem.characters(5) }
sequence(:part_number) { |n| "GA#{n}" }
featured false
active false
sequence(:weighting) { |n| n }
single false
association :category
factory :product_skus do
after(:build) do |product, evaluator|
build_list(:sku, 3, product: product)
end
end
end
end
product_spec.rb (unit test)
require 'spec_helper'
describe Product do
describe "Setting a product as a single product" do
let!(:product) { build(:product_skus, single: true) }
context "when the product has more than one SKU" do
it "should raise an error" do
expect(product).to have(1).errors_on(:single)
end
end
end
end
As you can see from the singe_product method, I'm trying to trigger an error on the single attribute when the single attribute is set to true and the product has more than one associated SKU. However, when running the test the product has no associated SKUs and therefore fails the unit test shown above.
How do I build a record and generate associated SKUs which can be counted (e.g: product.skus.count) and validated before they are all created in FactoryGirl?
You could write this like
it 'should raise an error' do
product = build(:product_skus, single: true)
expect(product).not_to be_valid
end
I've wrote the follow example:
it "should assign #services containing all the current user services" do
customer = FactoryGirl.create(:user, fullname: "Iris Steensma")
sign_in customer
service = FactoryGirl.create(:service, user: customer)
puts "Service.count = #{Service.count}" # Service.count = 0
get :home
assigns[:services].should eq([service])
end
The action controller as:
def home
##services = curent_user.posted_services
#services = Service.all
end
And factories.rb contains:
FactoryGirl.define do
sequence :address do |n|
"Street #{n}"
end
factory :user do
fullname "Foo Bar"
email { "#{fullname.gsub(' ', '.').downcase}#example.com" if fullname }
password "secret"
end
factory :preference do
profile "customer"
user
end
factory :service do
status :pending
source_addr { generate(:address) }
target_addr { generate(:address) }
passenger "Mis Daysi"
start_at Time.now
offer 5
payment "cash"
user
end
end
Why Factory Girl can't create the Service record? The factory works fine in the test environment "rails c test"
Here is the rspec ouput:
Failures:
1) UsersController GET home should assign #services containing all the current user services
Failure/Error: assigns[:services].should eq([service])
expected: [#<Service:0x460d8ea #name="Service_1003">]
got: []
(compared using ==)
Diff:
## -1,2 +1,2 ##
-[#<Service:0x460d8ea #name="Service_1003">]
+[]
# ./spec/controllers/users_controller_spec.rb:26:in `block (3 levels) in <top (required)>'
Finished in 1.03 seconds
3 examples, 1 failure
I believe the correct syntax is:
assigns(:service).should eq([service])
According to the rspec documentation assigns[key] used to be the way to assign instance variables which looks a little like what's happening for you.
https://github.com/rspec/rspec-rails
First, the user: customer here is not needed, since you are using customer.services.create that already does that
service = customer.services.create(attributes_for(:service, user: customer))
Second, try this, after service = customer.services.create do something like
service.valid?
puts service.errors.inspect
puts service.user == customer
maybe you can also try
service = FactoryGirl.create(:service, :user => customer)
also, are you sure the association is defined on Service class?
doesn't
#<Service:0x57856f4 #name="Service_1003">
should be
#<Service:0x57856f4 #name="Service_1003" user="<your customer object">
?
how is your factory for service defined?