Reputation: 2884
I'd like to test the uniqueness of entries on a city
model.
class City < ActiveRecord::Base
validates :name, uniqueness: { scope: :country_code,
message: "This city has already been added" }
end
I need to create a spec for testing this validation. My thinking is that the test will be along these lines:
my_city = FactoryGirl.create(:city)
context "when inserting" do
it "cannot have the same name and country code" do
expect{create :my_city}.to raise_error(ActiveRecord::DuplicateRecord)
end
end
However, I've been unable to figure out how to use FactoryGirl to create a duplicate instance of the my_city
object. Simply using create :my_city
as demonstrated in the above snippet results in:
ArgumentError: Factory not registered: my_city
Edit
This is what my City
factory looks like:
# spec/factories/city_factory.rb
FactoryGirl.define do
factory :city do
name { Faker::Address.city}
country { CountryCodes.find(Faker::Address.country_code)}
latitude { Faker::Number.decimal(2, 6)}
longitude { Faker::Number.decimal(2, 6)}
population { Faker::Number.number([3, 6, 7].sample) }
timezone { Faker::Address.time_zone }
end
end
So simple to run create :city
twice, will result in two completely different cities being inserted. I need to test the same city, twice.
Upvotes: 0
Views: 983
Reputation: 7956
If you don't want to use shoulda-matchers
you could do this simply by using FactoryGirl
to create the first city, then building another city with the same name
and country_code
. Finally, test the error messages it produces when validity is checked:
city = create(:city)
invalid_city = City.new(country_code: city.country_code, name: city.name)
expect(invalid_city).not_to be_valid
expect(invalid_city.errors.full_messages).to include "This city has already been added"
Upvotes: 1
Reputation: 3180
Well, I cannot put it in better words than the error tells you. You do not have a factory called my_city
registered. If you would like to create a duplicate record, you simply call the create
method twice and pass the duplicate attributes that you'd like to test. For example, you can test this in the following ways:
it "cannot have the same name and country code" do
first_city = create :city
duplicate_city = build(:city, name: first_city.name, country_code: first_city.country_code)
expect(duplicate_city).not_to be_valid
end
it "cannot have the same name and country code" do
attrs = FactoryGirl.attributes_for :city
first_city = create :city, attrs
duplicate_city = build(:city, attrs)
expect(duplicate_city).not_to be_valid
end
it "cannot have the same name and country code" do
first_city = create :city
expect{create (:city, name: first_city.name, country_code: first_city.country_code)}.to raise_error(ActiveRecord::DuplicateRecord)
end
Note that every time that you call the create
method, you are creating a new record so the second time you call it there will already be an existing record in the database and the only attribute that will be different is the id
. The exception to this is if you have declared a sequence
in your factory or some other method call which will vary at time of test execution such as Date.today
Upvotes: 1
Reputation: 2031
In general, testing uniqueness is pretty simple. Let the shoulda matchers do the work for you. Once you have a factory you can do this...
specify {
create :city;
is_expected.to validate_uniqueness_of(:name).scoped_to(:country_code) }
You must have at least one record created. Then the shoulda matcher will use that record, read its attributes, and make a duplicate to test your validation.
Upvotes: 2
Reputation: 249
You can test your validation like this:
RSpec.describe City do
let(:city) { create :city }
subject { city }
it { should validate_uniqueness_of(:name).scoped_to(:country_code) }
end
But first you need to build your factory (on a separate file )for City like this:
FactoryGirl.define do
factory :city do
sequence(:name) { |n| Faker::Lorem.word + "(#{n})" }
end
end
Upvotes: 2