I am having issues testing a rather simple thing, i.e. duplicity of codes not being allowed in the db.
Let's say I have this model:
class Ticket < ActiveRecord::Base
attr_accessible :code, :series, :sec_code
validates_uniqueness_of :code, scope:[:series,:sec_code]
before_validation :generate_codes, on: :create
private
def generate_codes
self.code=rand.to_s[2..9] #-> 8 digit code
self.sec_code=rand.to_s[2..4] #-> 2 digit security code
self.series=('A'..'Z').to_a.sample #-> one character series
end
end
Then I would like to test that the uniqueness validation does work, in this way:
require 'spec_helper'
describe Ticket do
before do
@ticket=Ticket.create
end
subject{@ticket}
it{ should respond_to(:code)}
...
describe ", duplicate codes" do
before do
dup_ticket=@ticket.dup
#Here lays the problem since this calls `generate_codes`
#before saving, so it ends up not being a duplicate!
dup_ticket.save #<---
end
it{ should_not be_valid}
end
end
I was considering the use of after_initialize
, but this is called everytime the object is instantiated, i.e. after being extracted from the DB, which is not desired.
I was wondering, why is generate_codes
called even when there is an on: :create
at the filter callback?
Inspired by jklina's answer/comment, I found out that using update_attribute
helps in this case, and I can update the field without triggering the before validation callbacks.
So the test turns into:
require 'spec_helper'
describe Ticket do
before do
@ticket=Ticket.create
end
subject{@ticket}
it{ should respond_to(:code)}
...
describe ", duplicate codes" do
before do
dup_ticket=@ticket.dup
dup_ticket.update_attribute(:code, @ticket.code)
dup_ticket.update_attribute(:sec_code, @ticket.sec_code)
dup_ticket.update_attribute(:series, @ticket.series)
end
it{ should_not be_valid}
end
end
Hence reflecting the same value for code
, sec_code
, and series
at the ActiveRecord table. And the test does what it's expected it to.