undefined method `size' whit shoulda-matchers and Rspec Rails - ruby-on-rails

i try test a simples validation validate_presence_of using should-matchers and Rspec and FactoryGirls but when i run get this error:
undefined method `size' for nil:NilClass
My test is:
context "when create new user" do
let(:consumer) { create(:consumer) }
it { should validate_presence_of(:name) }
end
in my Consumer.rb i have:
validates_presence_of :name, :email, :cpf, :phone_number, :birthday, :message => :attrs_blank,:on => :create
and my FactoryGirls have:
FactoryGirl.define do
factory :consumer do
association :vendor
association :branch
association :client
name { Faker::Name.name }
..
i try use subject { FactoryGirl.create(:consumer) } in place of let(:consumer) { create(:consumer) } but get the same error, what is wrong ?
UPDATE:
complete error:
Consumer when create new user should require name to be set
Failure/Error: it { should validate_presence_of(:name) }
NoMethodError:
undefined method `size' for nil:NilClass
# ./app/models/consumer.rb:58:in `valid_phone?'
this validation is the problem:
validate :valid_phone?
apparently this is the problem, the phone_number is nil:
def valid_phone?
#p = self.phone_number
#bool = true
if #p.size == 11 && #p[2] != "9"
#bool = false
elsif #p[0].eql?("0")
#bool = false
elsif #p.length > 11 || #p.length < 10
#bool = false
end
errors.add(:phone_number,:error_phone) if #bool == false
end
I modified the test to:
let(:consumer) { create(:consumer,:phone_number => "5182969611") }
but the problem persists.

Related

How to fix "NoMethodError: undefined method `cf_type' for nil:NilClass" for factorybot generated association

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

undefined method ` =' for #<Agency id: nil, name: nil, ip_adress: nil>

I am writing some rspec tests on my app and there is a bug, it show me that error :
undefined method ` =' for #<Agency id: nil, name: nil, ip_adress: nil>
There is my tests :
require 'rails_helper'
RSpec.describe Agency, type: :model do
it "should create the agency if all fields are filled" do
expect(FactoryGirl.build(:agency)).to be_valid
end
it "should fail if name is missing" do
expect(FactoryGirl.build(:agency, name: nil)).to_not be_valid
end
it "should fail if ip_adress is missing" do
expect(FactoryGirl.build(:agency, ip_adress: nil)).to_not be_valid
end
it "should fail if there is a double name in db" do
agency = FactoryGirl.create(:agency)
expect(FactoryGirl.build(:agency, name: agency.name)).to_not be_valid
end
end
My agency model :
class Agency < ActiveRecord::Base
module Agencymod
attr_accessor :name, :ip_adress
end
has_many :users
has_many :incidents
has_many :field_agency_agencies, dependent: :destroy
has_many :field_agencies, through: :field_agency_agencies
# # Regexp for the postal code.
# cp_regexp = /\A((0[1-9])|([1-8][0-9])|(9[0-8])|(2A)|(2B))[0-9]{3}\z/
# # Regexp for email.
# email_regexp = /\A[a-zA-Z0-9._-]+#[a-z0-9._-]{2,}\.[a-z]{2,4}$\z/
# # Regexp for phone number.
# phone_regexp = /\A(0|\+33|0033)[1-9][0-9]{8}\z/
# # Regexp for ip address.
ip_regexp = /\A(?:(?:[1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}(?:[1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\z/
validates :name, presence: true,
uniqueness: { case_sensitive: false }, length: { in: 0..44 }
validates :ip_adress, presence: true, format: { with: ip_regexp }, length: { in: 0..49 }
end
And finally my factory :
factory :agency, class: Agency do |f|
f.name  { Faker::Address.city }
f.ip_adress "8.8.8.8"
end
It is the first time this error appear and when I have tested the user model it works very well...
Sorry for my poor english :)
Thanks to everyone post answer.
I've found the error it appear there are an space between the f.name and the { Faker::Address.city }
When I have removed it, it show me the same error but with undefined method'name='... so I writed the factory like that :
f.name Faker::Address.city
and it works very well ...
My others factories are writted as below :
factory :user do |f|
f.surname { Faker::Name.first_name }
f.name { Faker::Name.last_name }
f.pseudo { Faker::Internet.user_name }
f.password "password"
f.email { Faker::Internet.free_email }
f.type_user_id 23
f.agency_id 2
f.tel "0606060606"
f.ip_addr { Faker::Internet.ip_v4_address }
end
and are working well too ..
f.name "TEST" # That works !
f.name"TEST" # Works too
f.name Faker::Address.city # Works
f.name { Faker::Address.city } # Nope
Issue in gem ?

shoulda factory girl error Couldn't find model without an ID

Good day, i get this error from
ActiveRecord::RecordNotFound:
Couldn't find User without an ID
my model
has_many :objects, class_name: 'OrderObject', dependent: :destroy
belongs_to :user
belongs_to :tariff
validates :client, :phone, :tariff_id, :days, :user_id, presence: true
spec
before do
user = FactoryGirl.create(:user)
FactoryGirl.create(:order, user_id: user.id)
end
context "validations" do
it { should validate_presence_of :client }
it { should validate_presence_of :phone }
it { should validate_presence_of :tariff_id }
it { should validate_presence_of :days }
end
it { should have_many(:objects) }
it { should belong_to(:tariff) }
it { should belong_to(:user) }
factory
factory :order do
client "MyString"
phone "MyString"
tariff_id 1
days 1
# advt_payed_day 1
# firm_payed_day 1
user_id 1
end
UPDATE 1
changed to
before(:all) do
user = FactoryGirl.create(:user )
puts user.id
order = FactoryGirl.create(:order, user_id: user.id )
puts order.id
end
output
Order
45
32
should have many objects
should belong to tariff
should belong to user
validations
should require client to be set (FAILED - 1)
should require phone to be set (FAILED - 2)
should require tariff_id to be set (FAILED - 3)
should require days to be set (FAILED - 4)
should require user_id to be set (FAILED - 5)
so order & user are created...
Update 2
as Rubyman suggested, i've changed couple of things:
in spec
before(:all) do
user = FactoryGirl.create(:user )
#puts user.id
order = FactoryGirl.create(:order, user_id: user.id )
puts order
puts order.user_id
end
in the factory
factory :order do
client "MyString"
phone "MyString"
tariff_id 1
days 1
# user_id 1
association :user, factory: :user
end
and output is:
Order
#<Order:0x00000005a866a0>
46
should have many objects
should belong to tariff
should belong to user
validations
should require client to be set (FAILED - 1)
should require phone to be set (FAILED - 2)
should require tariff_id to be set (FAILED - 3)
should require days to be set (FAILED - 4)
should require user_id to be set (FAILED - 5)
1) Order validations
Failure/Error: it { should validate_presence_of :client }
ActiveRecord::RecordNotFound:
Couldn't find User without an ID
# ./app/models/order.rb:35:in `user_is_not_admin?'
# ./spec/models/order_spec.rb:14:in `block (3 levels) in <top (required)>'
update 3
after reading advice from tdgs here are the changes:
in model no changes :
validates :client, :phone, :tariff_id, :days, :user_id, presence: true
in spec
describe Order do
before(:each) do
user = FactoryGirl.create(:user )
#puts user.id
order = FactoryGirl.create(:order, user_id: user.id )
puts order
puts order.user_id
puts order.tariff_id
puts order.phone
puts order.days
puts order.client
puts '*****'
user = User.find(order.user_id)
puts user.login
end
context "validations" do
it { should validate_presence_of :client }
it { should validate_presence_of :phone }
it { should validate_presence_of :tariff_id }
it { should validate_presence_of :days }
it { should validate_presence_of :user_id }
end
it { should have_many(:objects) }
it { should belong_to(:tariff) }
it { should belong_to(:user) }
end
output:
#<Order:0x00000006c10ce0>
161
101
MyString
1
MyString
*****
user__7
should require days to be set (FAILED - 1)
output for every should is valid as far as i see...
UPDATE N
should have written it in the beginning. i've run (hoped that it'll solve this issue) in console
bundle exec rake db:migrate
bundle exec rake db:migrate:reset db:test:prepare
First, your factory definition is not defining associations correctly. You should have something like this:
FactoryGirl.define do
factory :user do
sequence(:username) {|n| "username_#{n}"}
# more attributes here
end
factory :tariff do
# attributes
end
factory :order do
client "MyString"
phone "MyString"
tariff
user
days 1
end
end
Then your tests should be written like this:
context "validations" do
it { should validate_presence_of :client }
it { should validate_presence_of :phone }
it { should validate_presence_of :tariff_id }
it { should validate_presence_of :days }
end
it { should have_many(:objects) }
it { should belong_to(:tariff) }
it { should belong_to(:user) }
All the code you currently have in the before filter is not relevant right now. Also notice that using before(:all) might have some strange effects when running your tests, because they do not run inside a transaction. before(:each) on the other hand does.
try this
before {
#user = FactoryGirl.create(:user, :email => "test.com", :password => "test123", ... )
#order = FactoryGirl.create(:order, :user_id => #user.id )
}
Factory
require 'factory_girl'
FactoryGirl.define do
factory :order do
client "MyString"
...
...
end
end
Check how to create associations with factory girl
https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md
i moved away from shoulda and rewrote checks for validation. This way it works:
before(:each) do
#user = FactoryGirl.create(:user )
#order = FactoryGirl.create(:order, user_id: #user.id )
end
it 'absence of client isn\'t acceptable' do
temp = #order.client
#order.client = ''
#order.should_not be_valid
#order.client = temp
#order.should be_valid
end

rspec with factory girl

I have the following factories:
Factory.define :producer, :class => User do |f|
f.sequence(:email) { |n| "producer_#{n}#shit.com" }
f.password "foobar"
f.password_confirmation "foobar"
f.role "producer"
end
Factory.define :business do |f|
f.sequence(:name) { |n| "business_#{n}" }
f.association(:producer, :factory => :producer)
end
Factory.define :deal do |d|
d.sequence(:title) { |n| "deal_#{n}" }
d.sequence(:desc) { |n| "deal_desc_#{n}" }
d.cap "50"
d.rate "2"
d.start Date.today - 1 # This is the date where you put in db
d.end Date.today + 7
d.association :business
end
now when I do the following:
before(:each) do
#consumer = test_sign_in(Factory(:consumer))
#deal = Factory(:deal)
end
I am getting an error:
Failure/Error: #deal = Factory(:deal)
NoMethodError:
undefined method `producer=' for #<Business:0x007fb494290090>
# ./deals_controller_spec.rb:15:in `block (4 levels) in <top (required)>
(Line 15 refers to #deal = Factory(:deal) )
Does anyone know why? I am very new to factory girl and I can't seem to find the documentation explaining association and sequence very well.
The problem here is obviously linked to the creation of your producer association.
Since you're using the old dsl, I'd suggest two solutions:
Factory.define :business do |f|
f.sequence(:name) { |n| "business_#{n}" }
#try this:
f.association(:user, :factory => :producer)
#or this:
f.after_build { |biz| biz.user = Factory.build(:producer) }
end
The use of after_build or after_create is really a matter of choice, depending on your tests purposes.
Here is a link to the new dsl lookup.

rspec if giving a NoMethodError for Updating a User.field

Why does:
User.stuff_to_extract = 'boo'
work in the rails c
But in rspec it fails with this:
Failure/Error: #user1.stuff_to_extract = 'XXXXXX'
NoMethodError:
undefined method `stuff_to_extract=' for #<User:0x105cd4e60>
require 'factory_girl'
Factory.define :user do |f|
f.sequence(:fname) { |n| "fname#{n}" }
f.sequence(:lname) { |n| "lname#{n}" }
f.sequence(:email) { |n| "email#{n}#google.com" }
f.password "password"
f.password_confirmation { |u| u.password }
f.invitation_code "xxxxxxx"
f.email_signature_to_extract ""
end
In the first case you are calling the method on the User class. In the second you are calling it on a User instance. To fix the second example use:
User.stuff_to_extract = 'XXXXXX'
or redefine your function to be available to the instance:
class User
def stuff_to_extract= stuff
...
end
end
instead of being available to the class:
class User
def self.stuff_to_extract= stuff
...
end
end

Resources