I'm setting up a model for recordings with the following constraints
class Recording < ActiveRecord::Base
attr_accessible :agent_id, :confirmation, :filepath, :phone, :call_queue_id, :date
belongs_to :call_queue
PHONE_FORMAT = /^[0-9]+$|Unavailable/
validates_presence_of :call_queue_id, :agent_id, :phone, :filepath, :date
validates :phone, format: { with: PHONE_FORMAT }
end
and am trying to test it with the following spec
describe Recording do
let(:queue) { FactoryGirl.create(:call_queue) }
before { #recording = queue.recordings.build(FactoryGirl.attributes_for(:recording)) }
subject { #recording }
# Stuff omitted...
describe "phone" do
it "should be present" do
#recording.phone = ''
#recording.should_not be_valid
end
context "with a valid format" do
it "should only consist of digits" do
#recording.phone = 'ab4k5s'
#recording.should_not be_valid
end
it "should only match 'Unavailable'" do
#recording.phone = 'Unavailable'
#recording.should be_valid
end
end
end
end
The first two tests pass, but the third fails with the following:
Failure/Error: #recording.should be_valid
expected valid? to return true, got false
I tested my regex with rubular to make sure it was working, and then again using irb just to be sure. I'm really confused why this is failing.
EDIT:
I eventually got the specs to pass by changing my before statement in the rspec:
describe Recording do
let(:queue) { FactoryGirl.create(:call_queue) }
before(:each) { #recording = queue.recordings.create(FactoryGirl.attributes_for(:recording) }
# the rest is the same...
which ultimately makes sense to me, to a point. Was the reason everything was getting messed up (falses were returning true, and vice versa) because once an attribute made the record invalid, I couldn't change it again? It seems like that was the case, I just want to make sure.
try:
PHONE_FORMAT = /^([0-9]+|Unavailable)$/
Related
I'm having problems with the validate_inclusion_of matcher when writing a spec.
My current specs are to do with users and user groups. A user has a user group id.
I want to check that the user group id of a user is actually in the list of current user group ids.
At the moment my spec is basically:
describe 'company_user_group list of valid IDs' do
let!(:company_user_group_everything) { FactoryGirl.create(:company_user_group_everything) }
let!(:company_user_group_nothing) { FactoryGirl.create(:company_user_group_nothing) }
it '' do
#company_user = FactoryGirl.create(:company_user, id: 1)
CompanyUser.current_id = #company_user.id
is_expected.to validate_inclusion_of(:company_user_group_id).in_array(CompanyUserGroup.full_list_of_ids)
end
end
but the error I get is:
1) CompanyUser validations company_user_group list of valid IDs should validate that :company_user_group_id is either ‹2› or ‹3›
Failure/Error: is_expected.to validate_inclusion_of(:company_user_group_id).in_array(CompanyUserGroup.full_list_of_ids)
NoMethodError:
undefined method `attribute_setter' for nil:NilClass
I have tried various different things and debugged using byebug etc. but nothing is working for me.
e.g.
Adding in
#company_user_group_id = #company_user.company_user_group_id
and changing the is_expected.to line to
is_expected.to validate_inclusion_of(#company_user_group_id).in_array(CompanyUserGroup.full_list_of_ids)
I get the following error
1) CompanyUser validations company_user_group list of valid IDs should validate that :8 is either ‹8› or ‹9›
Failure/Error: is_expected.to validate_inclusion_of(#company_user_group_id).in_array(CompanyUserGroup.full_list_of_ids)
Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeDoesNotExistError:
The matcher attempted to set :8 on the CompanyUser to 8, but that
attribute does not exist
So it seems that the group id to check is valid (e.g. 8) and the array is valid (e.g. 8 and 9) but the matching isn't working.
Any help much appreciated!
Some of the company user model
# This model holds User identities for internal staff. This model is
# primarily intended for use in the xxxx application.
class CompanyUser LT ActiveRecord::Base
acts_as_authentic do |c|
c.merge_validates_uniqueness_of_email_field_options case_sensitive: false
c.merge_validates_length_of_password_field_options minimum: 8
if Rails.env.production?
c.logged_in_timeout = 30.minutes
else
c.logged_in_timeout = 90.minutes
end
end
# Constants
# relationships
belongs_to :company_user_group
# validations
validates :first_name, presence: true, length: { maximum: 20 }
validates :last_name, presence: true, length: { maximum: 20 }
validates :company_user_group_id, presence: true, numericality: { only_integer: true, greater_than: 0 }
validates :company_user_group_id, inclusion: { in: CompanyUserGroup.full_list_of_ids }, unless: 'Rails.env.test?'
validate :check_sys_admin_permission
If the "unless: 'Rails.env.test?'" bit is removed, most of the specs fail for some unknown reason. Just mentioned in case it is relevant.
and the following method from the Company Group model
# class methods
def self.full_list_of_ids
CompanyUserGroup.all.pluck(:id)
end
I think you want something like this:
it '' do
#company_user = FactoryGirl.create(:company_user, id: 1)
#company_user.company_user_group = company_user_group_everything
expect(#company_user).to validate_inclusion_of(:company_user_group_id).in_array(CompanyUserGroup.full_list_of_ids)
end
You might not even need to set the company user gropu of the company user.
But you've got
validates :company_user_group_id, inclusion: { in: CompanyUserGroup.full_list_of_ids }, unless: 'Rails.env.test?'
Which surely means it would fail because you don't do that in the test environment
I have figured it out. For some reason the validate_inclusion_of works when it is within a let! block.
It doesn't matter what argument is sent to the let helper method, just that one is.
Still don't know WHY this fixes it though, if anybody can enlighten me!?
So, the following spec now passes
describe 'company_user_group list of valid IDs' do
let!(:company_user_group_everything) { FactoryGirl.create(:company_user_group_everything) }
let!(:company_user_group_nothing) { FactoryGirl.create(:company_user_group_nothing) }
let!(:temp) do
it {is_expected.to validate_inclusion_of(:company_user_group_id).in_array(CompanyUserGroup.full_list_of_ids)}
end
end
I'm with the following problem:
Environment: Ruby: 2.3.1 and Rails 5.0.0.1
I'm trying to validate a datetime field with RSpec and Factory Girl.
I got this error:
expected: "2016-11-11 13:30:31 UTC" (From Factory Girl)
got: "2016-11-11T13:30:31.218Z" (From database)
My code:
klass_object = FactoryGirl.create(:character)
klass = Character
RSpec.shared_examples 'API GET #index' do |klass|
before { get :index, params: params, accept: Mime[:json] }
it "returns a list of #{klass.to_s.underscore.pluralize}" do
object_array = json(response.body)
klass_attributes = klass.attribute_names.without("id", "created_at", "updated_at").map(&:to_sym)
klass_attributes.each do |attribute|
object_array.each do |object|
expect(object[attribute].to_s).to eq(klass_object[attribute].to_s)
end
end
end
...
end
Factory:
FactoryGirl.define do
factory :character do
marvel_id { Faker::Number.number(6).to_i }
name { Faker::Superhero.name }
description { Faker::Hipster.paragraphs(1) }
modified { Faker::Date.between(DateTime.now - 1, DateTime.now) }
factory :invalid_character do
id ''
name ''
marvel_id ''
modified ''
end
end
end
How can I correct this problem?
I did that, it works but I think it is not so good. There is a better way to do it?
object_array.each do |object|
if ActiveSupport::TimeWithZone == klass_object[attribute].class
expect(object[attribute].to_datetime.strftime("%Y-%m-%d %H:%M:%S")).to eq(klass_object[attribute].to_datetime.strftime("%Y-%m-%d %H:%M:%S"))
else
expect(object[attribute].to_s).to eq(klass_object[attribute].to_s)
end
end
Thanks for your help.
I can suggest you to change your approach to compare the results. You can use approach, which based on the idea of the golden master.
In according to this approach you take a snapshot of an object, and then compare all future versions of the object to the snapshot.
In your case you can write json fixture first time, check that json is correct and compare it with result json next time.
For example
approved.json
[
{
"travel_time_seconds": 43200,
"available_seats_amount": 10,
"departure_at": "2016-04-08T02:00:00.000+03:00",
"arrival_at": "2016-04-08T17:00:00.000+03:00",
"source_point_name": "New York",
"destination_point_name": "Moscow",
"tickets_count": 2
}
]
controller_spec.rb
RSpec.shared_examples 'API GET #index' do |klass|
before { get :index, params: params, accept: Mime[:json] }
it "returns a list of #{klass.to_s.underscore.pluralize}" do
verify(format: :json) { json(response.body).map {|o| o.except('id', 'created_at', 'updated_at' }
end
...
end
approvals gem, for example, can help you with that
I know this is a very old question but I just came across the solution to this today and couldn't find another answer. I've been using Faker too, but the Date/Time formats don't seem to work very well with Ruby time math without a lot of finagling.
However, if you use Time in your factory, and then convert it .to_i, it will get sent to the db in the correct format for comparison in rspec.
Migration:
class CreateExperiences < ActiveRecord::Migration[5.2]
def change
create_table :experiences do |t|
t.datetime :start_date
Factory:
FactoryBot.define do
when_it_started = Time.zone.now - rand(3000).days
factory :experience do
start_date { when_it_started.to_i }
Spec:
RSpec.describe "Experiences API", type: :request do
let!(:valid_attributes) { attributes_for(:experience) }
describe "POST /v1/experiences" do
before { post "/v1/experiences", params: valid_attributes }
it "creates an experience" do
expect(JSON.parse(response.body).except("id", "created_at", "updated_at")).to eq(valid_attributes.stringify_keys)
end
Then my tests passed. Hopefully this will help someone else!
Try to use to_datetime instead to_s
expect(object[attribute].to_datetime).to eq(klass_object[attribute].to_datetime)
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 trying to test validations on my Rails 4 application. The problem is that adding one specific validation all test passes, regardless if remaining validations are commented or not. Here's my code:
# test/models/company_test.rb
setup do
#company = companies(:one)
#company2 = companies(:two)
end
test "should not save company with invalid name" do
#company.name = ""
assert !#company.save, "Saved company without name"
#company2.name = #company.name
assert !#company2.save, "Saved company without unique name"
#company.name = "a"
assert !#company.save, "Saved company with shorter name than 2 characters"
#company.name = rand(36**65).to_s(36)
assert !#company.save, "Saved company with longer name than 64 characters"
end
test "should not save company if website is invalid" do
#company.website = "foo"
assert !#company.save, "Saved company with invalid website"
end
# app/models/company.rb
validates :name, :owner_id, presence: true, uniqueness: true
validates :name, length: { minimum: 2, maximum: 64 }
validates :website, :format => URI::regexp(%w(http https))
Now if I comment out name validations, all tests still passes. And then if I comment out website validation also, then all tests fail.
Didn't find what's wrong with this particular code, but got around it:
I've used regular expression from here and placed it with format validation defined in Ruby on Rails guide.
# app/models/company.rb
validates :website, format: { with: /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+#)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+#)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%#.\w_]*)#?(?:[\w]*))?)/, message: "invalid url" }
I'm new and trying to learn Rails using the book Agile Web Development with Rails, Fourth Edition (for Rails 3.2). Been able to get through all of the chapters so far without hiccups. If there were errors, it was usually my sloppy code (forgetting a comma, 'end' statement, etc.). But now I've hit a snag in the chapter on Unit Testing for Models. At the part where we're validating that the image URL ends with either .gif, .jpg, or .png.
I copied the code verbatim from the book for the depot/test/product_test.rb file:
test "image url" do
ok = %w{ fred.gif fred.jpg fred.png FRED.JPG FRED.Jpg http://a.b.c/x/y/z/fred.gif }
bad = %w{ fred.doc fred.gif/more fred.gif.more }
ok.each do |name|
assert new_product(name).valid?, "#{name} shouldn't be invalid"
end
bad.each do |name|
assert new_product(name).invalid?, "#{name} shouldn't be valid"
end
But when I run the rake test:units command, I get the following failure...
1) Failure:
test_image_url(ProductTest)[../depot/test/unit/product_test.rb:46]:
fred.gif shouldn't be invalid
4 tests, 13 assertions, 1 failures, 0 errors, 0 skips
rake aborted!
Does this mean that the image URL it's testing is invalid? Why is the test failing if what it says "fred.gif shouldn't be invalid" is correct?
I'm pretty confident that it's this part of the test that must be incorrect because the other tests I have in there (ex. "product attributes must not be empty", "product price must be positive", etc.) run just fine. I get no failures if I take out the "test image url" code block.
Please let me know what I'm doing wrong. If you need me to post the entirety of the ProductTest, I can.
UPDATE: There was a typo in my Products model that was causing the test to fail. All fixed now.
I had the same problem and I had to change my definition for new_product. In my initial definition, I had quotation marks around 'image url'. Once I removed the quotes, I was fine. Here's the code (my initial mistake was on the fifth line of my code):
def new_product(image_url)
Product.new(title: "My Book Title",
description: "yyy",
price: 1,
image_url: image_url)
end
test "image url" do
ok = %w{ fred.gif fred.jpg fred.png FRED.JPG FRED.Jpg
http://a.b.c/x/y/z/fred.gif }
bad = %w{ fred.doc fred.gif/more fred.gif.more }
ok.each do |name|
assert new_product(name).valid?, "#{name} shouldn't be invalid"
end
bad.each do |name|
assert new_product(name).invalid?, "#{name} shouldn't be valid"
end
end
As you can see, I didn't use the '_' in my test name and my tests passed.
I believe the problem is in the definition for function 'new_product'. Make sure the function is setting all fields to valid data. Mine wasn't setting the description to a valid value. I hope this helps. You probably already found this solution already but didn't update your post.
What I'm saying is that the product is failing due to some other field. The test that's failing is checking that there are no errors in a product constructed with each image_url. You just need to check that the constructor function, new_product, can construct a valid product.
Seems like you are missing underscore _
test "image url" do
should be
test "image_url" do
You can the paragraph from my file here:
test "image_url" do
ok = %w{ fred.gif fred.jpg fred.png FRED.JPG FRED.Jpg http://a.b.c/x/y/z.gif}
bad = %w{ fred.doc fred.gif/more fred.gif.more }
ok.each do |name|
assert new_product(name).valid?, "#{name} shouldn't be invalid"
end
bad.each do |name|
assert new_product(name).invalid?, "#{name} shouldn't be valid"
end
I was just struggling with this. There was also a typo in model. With my validations. I grouped the \Z with the png. It needs to be outside the capture group.
wrong:
class Product < ApplicationRecord
validates :title, :description, :image_url, presence: true
validates :price, numericality: { greater_than_or_equal_to: 0.01 }
validates :title, uniqueness: true
validates :image_url, allow_blank: true, format: {
with: %r{\.(gif|jpg|png\Z)}i, # <<<<<----
message: 'must be a URL for GIF, JPG, or PNG image.'
}
end
right:
class Product < ApplicationRecord
validates :title, :description, :image_url, presence: true
validates :price, numericality: { greater_than_or_equal_to: 0.01 }
validates :title, uniqueness: true
validates :image_url, allow_blank: true, format: {
with: %r{\.(gif|jpg|png)\Z}i, # <<<<<----
message: 'must be a URL for GIF, JPG, or PNG image.'
}
end