I have fairly simple model but I'm getting RSpec failures that I cannot find the solution to.
Model -
store_accessor :properties, :fields
store_accessor :priorities
belongs_to :user
belongs_to :external_service
validates :user, :external_service, presence: true
validate :service_enabled?
validates_presence_of :properties, message => "This field is non editable"
Rspec -
require 'rails_helper'
module Application
describe Integration, type: :model do
it { should validate_presence_of(:user) }
it { should validate_presence_of(:external_service) }
it { should validate_presence_of(:properties) }
context 'external service' do
let(:service) { Application::ExternalService.new }
before do
allow(subject).to receive(:external_service).and_return(service)
end
end
end
end
This is the failure that I am getting:
Failure -
Application::Integration should require properties to be set
Failure/Error: it { should validate_presence_of(:properties) }
Expected errors to include "can't be blank" when properties is set to nil,
got errors:
* "can't be blank" (attribute: user, value: nil)
* "can't be blank" (attribute: external_service, value: nil)
* "The service must be enabled to add an integration." (attribute: external_service, value: nil)
* "This field is non editable" (attribute: properties, value: nil)
By default validates_presence_of consider error message as can't be blank. but, as you are setting custom validation message then, you have to verify the same using with_message in spec
it { should validate_presence_of(:properties).with_message('This field is non editable') }
Example for validate_presence_of with with_message
Related
I am trying rspec and shoulda matchers as my new testing framework and I'm a little confused as to how to get the should validate_uniqueness_of(:attribute) test to pass. Here is my simple model:
class Employee < ActiveRecord::Base
validates :name, presence: true
validates :title, presence: true
validates :department, presence: true
validates :avatar, presence: true
validates :email, presence: true, uniqueness: true
validates :reports_to, presence: true
mount_uploader :avatar, EmployeeAvatarUploader, on: :file_name
end
And here is the initial test:
RSpec.describe Employee, type: :model do
it { should validate_presence_of(:name) }
it { should validate_presence_of(:title) }
it { should validate_presence_of(:department) }
it { should validate_presence_of(:avatar) }
it { should validate_presence_of(:email) }
it { should validate_uniqueness_of(:email) }
it { should validate_presence_of(:reports_to) }
end
This fails with a description of the error pasted below:
`1) Employee should validate uniqueness of email
Failure/Error: should validate_uniqueness_of(:email)
Shoulda::Matchers::ActiveRecord::ValidateUniquenessOfMatcher::ExistingRecordInvalid:
validate_uniqueness_of works by matching a new record against an
existing record. If there is no existing record, it will create one
using the record you provide.
While doing this, the following error was raised:
PG::NotNullViolation: ERROR: null value in column "title" violates not-null constraint
DETAIL: Failing row contains (21, null, null, null, null, null, null, 2017-06-27 01:07:49.125445, 2017-06-27 01:07:49.125445, null, null).
: INSERT INTO "employees" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"
The best way to fix this is to provide the matcher with a record where
any required attributes are filled in with valid values beforehand.`
So my impression from the documentation was that my test should work as is. From the shoulda matcher documentation, I've tried this version of my test:
RSpec.describe Employee, type: :model do
it { should validate_presence_of(:name) }
it { should validate_presence_of(:title) }
it { should validate_presence_of(:department) }
it { should validate_presence_of(:avatar) }
it { should validate_presence_of(:email) }
it 'should validate uniqueness of email' do
email1 = FactoryGirl.create(:account, email: 'email#blackducksoftware.com')
email2 = FactoryGirl.build(:account, email: 'email#blackducksoftware.com' )
should validate_uniqueness_of(:email)
end
it { should validate_presence_of(:reports_to) }
end
This didn't seem to work either. Then I tried a variation of this test found by clicking Rspec validates_uniqueness_ofthis link
But still no luck. Could someone please direct me to the proper way of making this validation work?
You should be able to call it this way:
describe "validations" do
subject { Employee.new(title: "Here is the content") }
it { should validate_uniqueness_of(:email) }
end
Or with FactoryGirl:
describe "validations" do
subject { FactoryGirl.build(:employee) }
it { should validate_uniqueness_of(:email) }
end
Your employees table has a database level constraint requiring title, so you need to create a valid employee object first.
It looks as though in your second example you're building a account instead of employee.
See docs here: docs
describe Employee, type: :model do
context 'validations' do
it { should validate_uniqueness_of(:email) }
end
end
Here, subject is implicit. A new instance of Employee is built(not created) and validation is verified.
I'm using rails4, factory_ girl, rspec and shoulda matchers. If I run rspec with the code below I get this error:
Product should validate that :website cannot be empty/falsy, producing a custom validation error on failure
Failure/Error: self.website = "http://#{self.website}" unless self.website[/^https?/]
NoMethodError:
undefined method `[]' for nil:NilClass
If I delete unless self.website[/^https?/] from the format_website method I get this error:
Product did not properly validate that :website cannot be empty/falsy,
producing a custom validation error on failure.
After setting :website to ‹nil› -- which was read back as ‹"http://"›
-- the matcher expected the Product to be invalid and to produce a
validation error matching ‹/can't be blank/› on :website. The record
was indeed invalid, but it produced these validation errors instead:
* user: ["can't be blank"]
* name: ["can't be blank"]
* company: ["can't be blank"]
What should I do to make this work?
product model
belongs_to :user
validates :name, presence: { message: "can't be blank" }, length: { maximum: 140, message: "can't be longer than 140 characters" }, uniqueness: { message: "already exists" }
validates :company, presence: { message: "can't be blank" }, length: { maximum: 140, message: "can't be longer than 140 characters" }
validates :website, presence: { message: "can't be blank" }, length: { maximum: 140, message: "can't be longer than 140 characters" }
before_validation :format_website
validate :website_validator
def format_website
self.website = "http://#{self.website}" unless self.website[/^https?/]
end
def website_validator
self.errors.add :website, "format is invalid!" unless website_valid?
end
def website_valid?
!!website.match(/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-=\?]*)*\/?$/)
end
factory
FactoryGirl.define do
factory :product do
name { Faker::Commerce.product_name }
company { Faker::Company.name }
website { 'https://example.com' }
user
end
end
it { is_expected.to callback(:format_website).before(:validation) } #this one is not important, if I take out it still gives the same error
it { is_expected.to validate_presence_of(:name).with_message(/can't be blank/) }
it { is_expected.to validate_presence_of(:company).with_message(/can't be blank/) }
it { is_expected.to validate_presence_of(:website).with_message(/can't be blank/) }
it { is_expected.to belong_to(:user) }
You should ensure that website is not nil.
def format_website
return if website.blank?
self.website = "http://#{self.website}" unless self.website[/^https?/]
end
In that case, if self.website == nil, you'll try to call the method [] on a nil object, hence the first error.
For the second case, the answer is in the return you have from rspec:
After setting :website to ‹nil› -- which was read back as ‹"http://"›
Your method format_website returns "http://", which is because website is nil.
I'm learning rspec, I try to valitade length of my model, but I have this errors and I don't know what is going wrong?
#factories/user
FactoryGirl.define do
factory :user do
first_name {"Name"}
last_name {"Surename"}
phone_number {"1245767"}
email {"email#email.net"}
password {"password"}
end
end
user_spec: it { should validate_length_of(:phone_number).is_at_least(7)}
user model: validates_length_of :phone_number, minimum: 7
error:
User validations should ensure phone_number has a length of at least 7
Failure/Error: it { should validate_length_of(:phone_number).is_at_least(7)}
Did not expect errors to include "is too short (minimum is 7 characters)" when phone_number is set to "xxxxxxx",
got errors:
* "can't be blank" (attribute: email, value: "")
* "can't be blank" (attribute: password, value: nil)
* "is too short (minimum is 7 characters)" (attribute: phone_number, value: 0)
* "can't be blank" (attribute: first_name, value: nil)
* "can't be blank" (attribute: last_name, value: nil)
thank you
#edit
require 'rails_helper'
describe User do
describe 'validations' do
it { should validate_presence_of :first_name }
it { should validate_presence_of :last_name }
it { should validate_presence_of :phone_number }
it { should validate_length_of(:phone_number).is_at_least(7)}
end
end
When using an implicit subject, RSpec instantiates the class for you, e.g.:
describe User
it { should_blah_blah }
end
At this point the subject (meaning the thing referred to by it) is an instance of User created by calling User.new. This is convenient for some unit tests, but in your case you want to initialize the user with a factory. Use the explicit subject, e.g.:
describe User
subject { build(:user) }
it { should_blah_blah }
end
Now the subject is a User instance initialized from the factory definition.
More info in the RSpec docs
I would like to test my models but all informations that I could find seems to be outdated. My goal is to test each individual validation.
My model:
class Author < ActiveRecord::Base
has_and_belongs_to_many :books
before_save :capitalize_names
validates :name, :surname, presence: true, length: { minimum: 3 },
format: { with: /[a-zA-Z]/ }
private
def capitalize_names
self.name.capitalize!
self.surname.capitalize!
end
end
and my factorygirl define:
FactoryGirl.define do
factory :author do |f|
f.name { Faker::Name.first_name }
f.surname { Faker::Name.last_name }
end
end
So now, I want to test whether name is not shorter than 3 characters.
My context:
context 'when first name is too short' do
it { expect( FactoryGirl.build(:author, name: 'Le')).to
be_falsey }
end
I know it's invalid because of [FactoryGirl.build(:author, name: 'Le')] returns hash instead of boolean value. So now, how should I test it? What matcher should I use?
[SOLVED]
Use be_valid instead of be_falsey. Now it should look like :
context 'when first name is too short' do
it { expect( FactoryGirl.build(:author, name: 'Le')).not_to
be_valid }
end
I have been creating an application based on the Hartl course, and have added in the concept of Organization, which has_many users. The tests are all the standard tests Hartl recommends up to section 9.2 of the guide book. Since implementing Organizations into the application, one of the test cases is failing "when email address is already taken" - this should block a user from signing up with the same email address twice. What is odd is the fact that this is working in the application itself (form error - "users email address is already taken" thrown) but not in my tests. Can you help and indicate why this has broken please?
User code:
class User < ActiveRecord::Base
belongs_to :organization
#accepts_nested_attributes_for :organization
before_save { self.email = email.downcase }
before_create :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
validates :organization, presence: true
Organization code:
class Organization < ActiveRecord::Base
validates :organization_name, presence: true, length: { maximum: 50 }, uniqueness: true
has_many :users, :inverse_of => :organization
accepts_nested_attributes_for :users
User spec:
require 'spec_helper'
describe User do
before do
#user = FactoryGirl.create(:user)
end
subject { #user }
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should respond_to(:password_digest) }
it { should respond_to(:password) }
it { should respond_to(:password_confirmation) }
it { should respond_to(:remember_token) }
it { should respond_to(:authenticate) }
it { should be_valid }
...
describe "when email address is already taken" do
before do
user_with_same_email = #user.dup
user_with_same_email.email = #user.email.upcase
user_with_same_email.save
end
it { should_not be_valid }
end
Factory Girl Code:
FactoryGirl.define do
factory :organization do
organization_name "Example Org"
trait :wrong do
organization_name "Wrong Org"
end
trait :also_wrong do
organization_name "Another Wrong Org"
end
end
factory :user do
association :organization
name "Example Name"
email "email#example.com"
password "foobar"
password_confirmation "foobar"
trait :wrong_org do
association :organization, :factory => [:organization, :wrong]
end
trait :wrong_org2 do
association :organization, :factory => [:organization, :also_wrong]
end
end
end
The error thrown from the Rails console is as follows:
1) User when email address is already taken should not be valid
Failure/Error: it { should_not be_valid }
expected #<User id: 5287, name: "Example Name", email: "email#example.com", created_at: "2014-07-22 15:04:33", updated_at: "2014-07-22 15:04:33", password_digest: "$2a$04$jrxyuz9e574BoaAhZm6xkOUeAY5spyDut2CCEvAykMu...", organization_id: 5025, remember_token: "339dfafcac7bc5925dbf4e44f60a782f3bbbaa1b">.valid? to return false, got true
I've tried changing the code inside the test, but no matter what I do it keeps throwing an error. As mentioned above, when I open up the application in my local server I can use all functions, and when I try to sign up using a duplicate email address it won't let me. What's wrong with my test code?
The #user is completely valid:
You create your subject, #user. This is valid
You create the user_with_same_email
That user is not valid, because it has the same email as #user
Saving user_with_same_email returns false, but that is not checked in your test
The duplicate user is not saved in the db
The original user is still valid
A correct test would just #dupthe user (or make a new one with the same email), and then check that the new record is not valid.