I am testing my app ( Rails 5) with rspec capybara and factory girl I have the following error...
I am not sure what's happening... I am very new with rspec I hope you could help me :) thank you
Randomized with seed 41137
An error occurred in a `before(:suite)` hook.
Failure/Error: FactoryGirl.lint
SystemStackError:
stack level too deep
You will find my code below:
factories.rb
FactoryGirl.define do
factory :event do
name {Faker::Friends.character}
total_price 50
participant
end
factory :participant do
first_name { Faker::Name.first_name }
salary 900
event
end
end
event.rb
class Event < ApplicationRecord
has_many :participants, inverse_of: :event
validates :participants, presence: true
validates :name, presence: true, length: {minimum: 2}
validates :total_price, presence: true
accepts_nested_attributes_for :participants, reject_if: :all_blank, allow_destroy: true
def total_salary
all_salary = []
participants.each do |participant|
all_salary << participant.salary
end
return #total_salary = all_salary.inject(0,:+)
end
end
event_spec.rb
require 'rails_helper'
describe Event do
it { should have_many(:participants) }
it { should validate_presence_of(:participants) }
it { should validate_presence_of(:name) }
it { should validate_presence_of(:total_price) }
describe "#total_salary" do
it "should return the total salary of the participants" do
partcipant_1 = create(:participant, salary: 2000)
partcipant_2 = create(:participant, salary: 3000)
expect(partcipant_1.salary + partcipant_2.salary).to eq(5000)
end
end
end
edit
In my participant model I had to add optional: true
belongs_to :event, option: true
so fabriciofreitag suggestion works well :)
Let's take a look at your factories:
FactoryGirl.define do
factory :event do
name {Faker::Friends.character}
total_price 50
participant
end
factory :participant do
first_name { Faker::Name.first_name }
salary 900
event
end
end
In this scenario, the creation of event will create a participant, that will create an event, that will create a participant. and so on, in an infinite loop (stack level too deep).
Perhaps you could change it to something like this:
FactoryGirl.define do
factory :event do
name {Faker::Friends.character}
total_price 50
participants { create_list(:participant, 3, event: self) }
end
factory :participant do
first_name { Faker::Name.first_name }
salary 900
end
end
Related
I get the following rspec errors because the association CustomField is nil, but it should be generated by Factorybot and I cannot figure out why it is not. The project association works just fine.
Rspec Tests:
it { should belong_to(:custom_field) }
it { should validate_presence_of(:custom_field) }
Rspec Errors:
NoMethodError: undefined method `cf_type' for nil:NilClass
0) CustomFieldValue validations should validate that :custom_field_id cannot be empty/falsy
Failure/Error: ["single_select", "multi_select", "radiobutton", "checkbox", "label"].include?(custom_field.cf_type)
NoMethodError:
undefined method `cf_type' for nil:NilClass
# ./app/models/custom_field_value.rb:13:in `option_field?'
# ./spec/models/custom_field_value_spec.rb:18:in `block (3 levels) in <top (required)>'
NoMethodError: undefined method `cf_type' for nil:NilClass
0) CustomFieldValue validations should belong to custom_field required: true
Failure/Error: ["single_select", "multi_select", "radiobutton", "checkbox", "label"].include?(custom_field.cf_type)
NoMethodError:
undefined method `cf_type' for nil:NilClass
# ./app/models/custom_field_value.rb:13:in `option_field?'
# ./spec/models/custom_field_value_spec.rb:20:in `block (3 levels) in <top (required)>'
Model: CustomFieldValue
class CustomFieldValue < ApplicationRecord
belongs_to :custom_field
belongs_to :project
validates :custom_field, :project, presence: :true
validate :options_existence, if: :option_field?
validate :allowed_value_for_option_fields, if: -> cfv { cfv.option_field? && cfv.options_exist? }
# Conditions for validations
def option_field?
["single_select", "multi_select", "radiobutton", "checkbox", "label"].include?(custom_field.cf_type)
end
def options_exist?
!custom_field.custom_field_options.empty?
end
private
# Custom validations
# Validate that option fields have at least 1 option defined
def options_existence
if custom_field.custom_field_options.empty?
errors.add(:base, "No custom field option was found")
end
end
# Validation that only custom field option id can be stored for custom fields with options defined
def allowed_value_for_option_fields
if !custom_field.custom_field_options.map{|cfo| cfo.id.to_s}.include?(string_value)
errors.add(:base, "Custom field option id is the only accepted value")
end
end
Model: CustomField
class CustomField < ApplicationRecord
enum cf_type: { string: 10, text: 20, number: 30, single_select: 40,
multi_select: 50, checkbox: 60, radiobutton: 70, date: 80,
single_user: 90, multi_user: 100, label: 110 }
belongs_to :organization
has_many :custom_field_values, dependent: :destroy
has_many :custom_field_options, dependent: :destroy
accepts_nested_attributes_for :custom_field_options
validates :cf_type, :name, :organization, presence: :true
end
Model: Project
class Project < ApplicationRecord
belongs_to :organization
has_many :custom_field_values, dependent: :destroy
validates :name, :key, :organization, presence: :true
validates :key, uniqueness: { case_sensitive: false }, length: { minimum: 2, maximum: 25 }
# Validation of format
# Valid Input: abcd, xyz, aaa, aa-bb
# Invalid Input: abC, XYZ, 123, ABC123, -abc, abc-
validates_format_of :key, :with => /\A(?!-)[a-z][-\w]+(?<!-)\z/
accepts_nested_attributes_for :custom_field_values
end
RSpec: custom_field_value_spec.rb
require "rails_helper"
RSpec.describe CustomFieldValue, :type => :model do
subject {
create(:custom_field_value)
}
context "validations" do
it { should validate_presence_of :project }
it { should validate_presence_of :custom_field }
it { should belong_to(:project) }
it { should belong_to(:custom_field) }
it { expect { create(:custom_field_value, number_value: 1) }.to raise_error(ActiveRecord::RecordInvalid, /Value can be set only for one of the string_value, text_value, number_value or date_value/) }
it { expect { create(:custom_field_value_for_checkbox) }.to raise_error(ActiveRecord::RecordInvalid, /Custom field option id is the only accepted value/) }
end
end
Factory: custom_field_values.rb (factorybot)
FactoryBot.define do
factory :custom_field_value do
sequence(:string_value) { |n| "My String#{n}" }
association :project
association :custom_field, cf_type: :string
end
factory :custom_field_value_for_checkbox, class: CustomFieldValue do
sequence(:string_value) { |n| "My String#{n}" }
association :project
association :custom_field, factory: :custom_field_with_custom_field_options, cf_type: :checkbox
end
end
Factory: custom_fields.rb (factorybot)
FactoryBot.define do
factory :custom_field do
cf_type { :string }
sequence(:name) { |n| "Name#{n}" }
sequence(:description) { |n| "Description#{n}" }
association :organization
factory :custom_field_with_custom_field_values do
transient do
custom_field_values_count { 3 }
end
after(:create) do |custom_field, evaluator|
create_list(:custom_field_value, evaluator.custom_field_values_count, custom_field: custom_field)
end
end
factory :custom_field_with_custom_field_options do
transient do
custom_field_options_count { 3 }
end
after(:create) do |custom_field, evaluator|
create_list(:custom_field_option, evaluator.custom_field_options_count, custom_field: custom_field)
end
end
factory :custom_field_with_custom_field_values_and_options do
transient do
custom_field_values_and_options_count { 3 }
end
after(:create) do |custom_field, evaluator|
create_list(:custom_field_value, evaluator.custom_field_values_and_options_count, custom_field: custom_field)
create_list(:custom_field_option, evaluator.custom_field_values_and_options_count, custom_field: custom_field)
end
end
end
end
Factory: projects.rb (factorybot)
FactoryBot.define do
factory :project do
sequence(:name) { |n| "Name#{n}" }
sequence(:key) { |n| random_name }
association :organization
end
end
def random_name(length=5)
source = ('a'..'z').to_a.shuffle.join
name = ""
length.times{ name += source[rand(source.size)].to_s }
return name
end
You can stub your conditional validation to make your test pass:
RSpec.describe CustomFieldValue, :type => :model do
subject {
create(:custom_field_value)
}
context "validations" do
before { allow(subject).to receive(:option_field?).and_return(false) }
it { should validate_presence_of :custom_field }
end
end
I am new to Factories and I need help for the association and nested attributes....
How do I set an admin user that creates a product? OK
How do I set category to a product? Ok
How do I attach images to a product? OK
How do I set product's sizes (nested attibutes)
user.rb
has_many :products
product.rb
belongs_to :user
belongs_to :category
has_many :sizes, inverse_of: :product, dependent: :destroy #nested_attributes
size.rb
belongs_to :product
category.rb
has_many :products
factories/users.rb
FactoryBot.define do
factory :user do
first_name { Faker::Name.first_name}
last_name { Faker::Name.last_name }
admin { [false, true].sample }
sequence(:email) { |n| "#{n}#{Faker::Internet.email}" }
birth_date {"20/10/1997"}
password { 'password'}
end
end
factories/categories.rb
FactoryBot.define do
factory :category do
title { Faker::Artist.name }
end
end
factories/sizes.rb
FactoryBot.define do
factory :size do
size_name {["S", "M", "L", "XL"].sample }
quantity { Faker::Number.number(2) }
end
end
factories/products.rb
FactoryBot.define do
factory :product do
title { Faker::Artist.name}
ref { Faker::Number.number(10)}
price { Faker::Number.number(2) }
color { Faker::Color.color_name }
brand { Faker::TvShows::BreakingBad }
description { Faker::Lorem.sentence(3) }
size
category
# how to set an admin ??
end
end
Add associations like this
For product
FactoryBot.define do
factory :product do
user {User.first || association(:user)}
user {User.first || association(:user, admin: true)}
# your admin attribute (role: admin or admin: true) whatever you are using for admin
category {Category.first || association(:category)}
end
end
Read FactoryBot association hope it will help.
I have a the following has_many model associations in my app:
User < Company < Deed < Subtransaction,
where Deed accepts_nested_attributes_for :subtransactions. I wish to test my Model validations using Minitest and fixtures.
I have trouble, however, testing the nested attributes for validity. For example , If I clear all nested attributes using
#deed.subtransactions.clear I correctly get a test response that the model is not valid.
However
#deed.subtransactions.first.num_shares = " " does not seem to work.
How do I properly test these nested attributes for validity?
My test:
class DeedTest < ActiveSupport::TestCase
def setup
#user = users(:dagobert)
#newco = companies(:newco)
params = { :deed =>
{
:date => deeds(:inc_new).date,
:subtransactions_attributes =>
{ '1' =>
{
:shareholder_name => "Shareholder 1",
:num_shares => subtransactions(:new_subt1).num_shares
},
'2' =>
{
:shareholder_name => "Shareholder 2",
:num_shares => subtransactions(:new_subt2).num_shares
}
}
}
}
#deed = #newco.deeds.new(params[:deed])
end
# test: does not raise any error messages
test "num_shares should be present for a subtransaction" do
#deed.subtransactions.first.num_shares = nil
assert_not #deed.valid?
end
# test passing: The params are submitted correctly and pass validation
test "fixture values should be valid" do
assert #deed.valid?
end
# test passing: I can test the validity of deed attributes
test "date should be present" do
#deed.date = " "
assert_not #deed.valid?
end
# test passing: when I clear #deed.subtransactions the object is no longer valid
test "a subtransaction should be present" do
#deed.subtransactions.clear
assert_not #deed.valid?
end
end
UPDATE
Deed model:
class Deed < ActiveRecord::Base
belongs_to :company
has_many :subtransactions, dependent: :destroy
accepts_nested_attributes_for :subtransactions, allow_destroy: true,
reject_if: ->(a) { a['shareholder_name'].blank? && a['num_shares'].blank? }
validates_associated :subtransactions
validates :date, presence: true
validates :subtransactions, presence: true
end
Subtransaction model:
class Subtransaction < ActiveRecord::Base
belongs_to :deed
belongs_to :shareholder
validates :num_shares, presence: true, length: { maximum: 50 },
:numericality => { only_integer: true }
validates :shareholder_name, presence: true
# end class
end
My associations aren't so complex but I've hit a wall making them work with FactoryGirl:
Text: blast_id:integer recipient_id:integer
class Text < ActiveRecord::Base
belongs_to :blast
belongs_to :recipient, class_name: "User"
validates :blast_id, presence: true
validates :recipient_id, presence: true
end
Blast: content:string author_id:integer
class Blast < ActiveRecord::Base
belongs_to :author, class_name: "User"
has_many :texts
validates :author_id, presence: true
end
User: name:string, etc. etc.
class User < ActiveRecord::Base
has_many :blasts, foreign_key: "author_id"
validates :name, presence: true
end
In FactoryGirl I've got:
FactoryGirl.define do
factory :user, aliases: [:author, :recipient] do |u|
sequence(:name) { Faker::Name.first_name }
end
factory :blast do
author
content "Lorem ipsum"
ignore do
texts_count 1
end
after :build do |blast, evaluator|
blast.texts << FactoryGirl.build_list(:text, evaluator.texts_count, blast: nil, recipient: FactoryGirl.create(:user) )
end
end
factory :text do
blast
association :recipient, factory: :user
end
end
Finally, some specs which all fail because Texts is not valid
require 'spec_helper'
describe Text do
User.destroy_all
Blast.destroy_all
Text.destroy_all
let!(:user) { FactoryGirl.create(:user) }
let!(:blast) { FactoryGirl.create(:blast, author: user) }
let(:text) { blast.texts.first }
subject { text }
it { should be_valid }
describe "attributes" do
it { should respond_to(:blast) }
it { should respond_to(:recipient) }
its(:blast) { should == blast }
its(:recipient) { should == recipient }
end
describe "when blast_id is not present" do
before { text.blast_id = nil }
it { should_not be_valid }
end
describe "when recipient_id is not present" do
before { text.recipient_id = nil }
it { should_not be_valid }
end
end
All the specs fail on FactoryGirl blast creation with:
1) Text
Failure/Error: let!(:blast) { FactoryGirl.create(:blast, author: user) }
ActiveRecord::RecordInvalid:
Validation failed: Texts is invalid
# ./spec/models/text_spec.rb:8:in `block (2 levels) in <top (required)>'
I've tried various iterations of the association code in the FactoryGirl docs and other question answers like this one but my situation is different enough that I can't get it to work.
If you've made it this far, thank you! Super grateful for any leads.
Your factory for "blast" should look like
factory :blast do
author
content "Lorem ipsum"
ignore do
texts_count 1
end
after :build do |blast, evaluator|
blast.texts << FactoryGirl.build_list(:text, evaluator.texts_count, blast: blast, recipient: FactoryGirl.create(:user) )
end
end
In other words, you immediately create the correct "parent" by connecting the newly created blast to the newly created tekst
To further dry your code, have a look at https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#configure-your-test-suite, describing how to get rid of using "FactoryGirl." over and over again by setting
config.include FactoryGirl::Syntax::Methods
once in your settings
I have a model item has_many ratings and a ratings belongs_to item ratings belongs_to user I want to force a user who is creating an item to rate it too. Other users can then rate it later on. item and user have no association in my model.
I am doing the following in my item_spec which is giving me an error no implicit conversion of Symbol into Integer on line #item = Item.new(name: "Item1", below.
class Item < ActiveRecord::Base
has_many :ratings, dependent: :destroy, inverse_of: :item
accepts_nested_attributes_for :ratings, :allow_destroy => true
validates :name , :length => { minimum: 3 }
validates :category , :length => { minimum: 3 }
validates_presence_of :ratings
end
require 'spec_helper'
describe Item do
before do
#item = Item.new(name: "Item1",
url: "www.item1.com",
full_address: "Item1Address",
city: "Item1City",
country: "Item1Country",
category: "Item1Type",
ratings_attributes: {"rating" => "3", "comment" => "Ahh Good"} )
end
Also using FactoryGirl I am doing something like this
factory :item do
before_create do |r|
r.ratings<< FactoryGirl.build(:ratings, item: r )
end
name "Item1"
url "www.Item1.com"
full_address "Item1Address"
city "Item1City"
country "Item1Country"
category "Item1Category"
end
factory :ratings do
rating 3
comment "Its not that bad"
user
end
end
which again is not yeilding the desired result.
can anyone help me solve this problem please.Thanks!
Working Code, now having problem testing some association order, but at least the desired functionality working.
factory :item do
name "Item1"
url "www.Item1.com"
full_address "Item1Address"
city "Item1City"
country "Item1Country"
category "Item1Category"
end
factory :ratings, :class => 'Ratings' do
association :item, factory: :item, strategy: :build
user
rating 3
comment "Its not that bad"
end
factory :item_with_rating, parent: :item do
ratings {[FactoryGirl.create(:ratings)]}
end
Here is the spec file
require 'spec_helper'
describe Item do
before do
#item = FactoryGirl.create(:item_with_rating)
end
subject { #item }
it { should respond_to(:name) }
it { should respond_to(:url) }
it { should respond_to(:full_address)}
it { should respond_to(:city) }
it { should respond_to(:country) }
it { should respond_to(:category) }
it { should respond_to(:ratings) }
it { should_not respond_to(:type) }
it { should_not respond_to(:user_id) }
it { should be_valid }
There is no change in the Model file for item