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" }
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 have a test:
test 'gift should not be saved with a non-numeric price' do
#gift = gifts(:gift_without_numeric_price)
assert_not #gift.save, 'gift was saved with non-numeric name'
end
It uses this fixture:
gift_without_numeric_price:
name: "pinecones"
description: "these pinecones smell really good"
estimated_price: "object"
link: "www.google.com"
My Gift model looks like this:
validates :estimated_price,
presence: true,
numericality: true
So I was expecting the test to pass since 'object' is not numeric, causing the #gift.save to return false and causing the assert_not to ultimately return true. However, the test fails for some reason. When I use the the direct way of creating a gift object, the test looks like this and the test passes:
test 'gift should not be saved with a non-numeric price' do
#gift = Gift.new(name: 'aa', description: 'description', link: 'www.google.com', estimated_price: 'object')
assert_not #gift.save, 'gift was saved with non-numeric name'
end
What am I doing wrong with the fixture?
You can try and change your validation:
validates :estimated_price, presence: true,numericality: { only_integer: true }
When trying to write a unit test for models I keep getting the same error and cant seem to fix it.
This is my test:
require 'test_helper'
class ProductTest < ActiveSupport::TestCase
test "product attirbutes must not be empty" do
product = Product.new
assert product.invalid?
assert product.errors[:title].any?
assert product.errors[:description].any?
assert product.errors[:price].any?
assert product.errors[:image_url].any?
end
test "product price must be positive" do
product = Product.new(title: "My Book Title",
description: "yyy",
image_url: "zzz.jpg")
product.price = -1
# line number 19 below
assert product.invalid?
assert_equal ["must be greater than or equal to 0.01"],
product.errors[:price]
product.price = 0
assert product.invalid?
assert_equal ["must be greater than or equal to 0.01"],
product.errors[:price]
product.price = 1
assert product.valid?
end
end
When I run > rake test
I get the following error:
1) Failure:
ProductTest#test_product_price_must_be_positive
/test/models/product_test.rb:19]:
Failed assertion, no message given.
Here is my model:
class Product < ActiveRecord::Base
validates :title, :description, :image_url, presence: true
validates :price, numericality: {greater_then_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
I have no idea what is going on here please help!
As per the MiniTest::Assertions docs, any method derived from 'assert' will raise the exception message "Failed assertion, no message given" on a fail unless you supply the optional 'msg' paramater
A few things:
you are testing Active Record validations, these are tried and tested, production ready features of a library so testing it is not necessary. To become more familiar with Active Record validations just go the docs and then play around with your models in the rails console
Active record validations are only performed when attempt to 'create' or 'save'
a model. For example:
my_user = User.create(name: nil, email: nil) # try save to DB - fails silently
my_user.valid? # => false
my_user.errors.messages # => {name:["can't be blank"], email:["can't be blank"]}
Maybe explore some futher learning on the topic, the Ryan Bate's screencasts are great and free for the most part. Hope this helps
Note: I hoped to attach some more links/refrences however I do not have the Stackoverflow points to do
your model is wrong.
(line 3)
↓
wrong) validates :price, numericality: {greater_then_or_equal_to: 0.01}
correct) validates :price, numericality: {greater_than_or_equal_to: 0.01}
not "then" but "than".
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)$/
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