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?

有帮助吗?

解决方案 2

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.

其他提示

You could try stubbing the generate_codes method to return what you want. So maybe adding:

allow(dup_ticket).to receive(:generate_codes).and_return(nil)

I can't test this, but this might prevent the record from generating new values. I'm not completely familiar with dup so you'll want to make sure that dup carries the code values over as well.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top