Home > Blockchain >  Rspecs for model and before_create methods
Rspecs for model and before_create methods

Time:01-20

I have model name: UserApplicationPatient. That model having two associations:

  belongs_to :patient
  belongs_to :customer

  before_create :set_defaults

  private

  def set_defaults
    self.enrol_date_and_time = Time.now.utc
    self.pap = '1'
    self.flg = '1'
    self.patient_num = "hos_#{patient_id}"
  end

Factories of UserApplicationPatient

FactoryBot.define do
  factory :user_application_patient do
    association :patient
    association :customer
    before(:create) do |user_application_patient, evaluator|
      FactoryBot.create(:patient)
      FactoryBot.create(:customer)
    end
  end
end

Model spec:

 require 'spec_helper'

 describe UserApplicationPatient do
   describe "required attributes" do
   let!(:user_application_patient) { described_class.create }

   it "return an error with all required attributes" do
    expect(user_application_patient.errors.messages).to eq(
     { patient:         ["must exist"],
       customer:        ["must exist"]
     },
    )
  end

 end
end

This is the first time I am writing specs of models. Could someone please tell me how to write specs for set_defaults before_create methods and factories what I have written is correct or not.

CodePudding user response:

Since you are setting default values in the before_create hook, I will recommend validating it like this

describe UserApplicationPatient do
   describe "required attributes" do
   
   let!(:user_application_patient) { described_class.create }

    it "return an error with all required attributes" do
      # it will validate if your default values are populating when you are creating a new object
      expect(user_application_patient.pap).to eq('1')
    end

  end
end

CodePudding user response:

To test your defaults are set, create a user and test if the defaults are set.

  • You definitely don't want to use let!, there's no need to create the object until you need it.
  • Since we're testing create there's no use for a let here at all.
  • How do we test Time.now? We can freeze time!
  • I assume patient_id should be patient.id.

Here's a first pass.

it 'will set default attributes on create' do
  freeze_time do
    # Time will be the same everywhere in this block.

    uap = create(:user_application_patient)

    expect(uap).to have_attributes(
      enrol_date_and_time: Time.now.utc,
      pap: '1',
      flg: '1',
      patient_num: "hos_#{uap.patient.id}"
    )
  end
end

it 'will not override existing attributes' do
  uap_attributes = {
    enrol_date_and_time: 2.days.ago,
    pap: '23',
    flg: '42',
    patient_num: "hos_1234"
  }
  uap = create(:user_application_patient, **uap_attributes)

  expect(uap).to have_attributes(**uap_attributes)
end

These will probably fail.

  • Defaults are set after validation has taken place.
  • Existing attributes are overwritten.
  • What is patient_id?

We can move setting defaults to before validation. That way the object can pass validation, and we can also see the object's attributes before writing it to the database.

We can fix set_defaults so it doesn't override existing attributes.

Time.now should not be used, it is not aware of time zones. Use Time.current. And there's no reason to pass in UTC, the database will store times as UTC and Rails will convert for you.

  belongs_to :patient
  belongs_to :customer

  before_validation :set_defaults

  private

  def set_defaults
    self.enrol_date_and_time ||= Time.current
    self.pap ||= '1'
    self.flg ||= '1'
    self.patient_num ||= "hos_#{patient.id}"
  end

We can also make your factory a bit more flexible.

FactoryBot.define do
  factory :user_application_patient do
    association :patient, strategy: :create
    association :customer, strategy: :create
  end
end

This way, the patient and customer will be created regardless whether you build(:user_application_patient) or create(:user_application_patient). This is necessary for user_application_patient to be able to reference its patient.id.

In general, don't do things at create time.

  • Related