Question

I am trying to write a test for a model which is working perfectly - but for some reason the test is not returning the expected result.

My model contains this:

after_validation :set_conference

  def rand_conference
    r = rand.to_s[2..8]
    while conference == r
        r = rand.to_s[2..8]
    end
    r
  end

  def set_conference
    self.conference = rand_conference unless conference.presence
  end
end

And in my model test I am testing this (ideally I would be testing if 'conference' is a 6 digit number, but for now just a 'not nil' would suffice):

Factory:

FactoryGirl.define do
  factory :plan do
    name "Test"
    description "Lorem Ipsum"
    category "template"
  end
end

Model_spec:

  before (:each) do
    @plan = build(:plan)
  end


 it "has a conference number" do
   expect(@plan.conference).not_to be_empty
 end

As a result i get:

1) Plan has a conference number
     Failure/Error: expect(@plan.conference).not_to be_empty
     NoMethodError:
       undefined method `empty?' for nil:NilClass

Does factorygirl use the after_validation callback?

Was it helpful?

Solution

I am trying to write a test for a model which is working perfectly - but for some reason the test is not returning the expected result.

The test is not returning the expected result because the after_validation callback is never called which is why conference attribute is nil. So @plan.conference is nil and you cannot call empty? on nil object. Hence, the error undefined method 'empty?' for nil:NilClass

@plan = build(:plan) would just create an instance of Plan model as per the attributes specified in factory :plan and assign it to instance variable @plan. It will definitely not run any validations and obviously no ActiveRecord callbacks.

Just change the before block as

before (:each) do
  @plan = create(:plan)  ## create record in database
end

With this your after_validation callback would be triggered as you actually create a record in database.

For reference:

Callbacks are methods that get called at certain moments of an object's life cycle. With callbacks it is possible to write code that will run whenever an Active Record object is created, saved, updated, deleted, validated, or loaded from the database.

OTHER TIPS

Yes the callbacks are automatically run. However, you used FactoryGirl.build to build a plan, which only initializes the object but doesn't validate it.

You either need to make sure the validations are run:

before (:each) do
  @plan = build(:plan)
end

it "has a conference number" do
  @plan.valid?
  expect(@plan.conference).not_to be_empty
end

Or you need to call FactoryGirl.create to create your plan, but this would also create it in your test database:

before (:each) do
  @plan = create(:plan)
end

it "has a conference number" do
  expect(@plan.conference).not_to be_empty
end

I think the most abvious way is to be explicit like this :

it  "has a conference number" do
  @plan = build(:plan)
  @plan.set_conference
  expect(@plan.conference).not_to be_empty
end

Anyway, I don't recommend to use callbacks. I think it's preferable to use service objects.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top