Friday, 16 January 2015

Testing the ActiveRecord models using Rspec

Usually the skeleton of my models spec looks like this:

require 'spec_helper'
                        
describe Car do
                        
  context 'class hierarchy' do
    #Here comes the class hierarchy specification
  end
                        
  context 'fields' do
  end
                        
  context 'assotiations' do
    #Here comes the enumeration of associations
  end
                        
  context 'validations' do
    #Here comes the validation of models
  end
                        
  context 'callbacks' do
    #Specs for callbacks
  end
                        
  context 'methods' do
  end
end

Testing the class hierarchy:
Since the model could include modules which affect the functionality I consider it necessary to assert on them.

describe Car do
  context 'class hierarchy' do
    specify {expect(subject.class).to be < ActiveRecord::Base}
    specify{expect(subject).to be_kind_of(Elasticsearch::Model)}
    specify{expect(subject).to be_kind_of(Elasticsearch::Model::Callbacks)}
  end
end

Testing the fields:
I often encountered errors when the model was expected to have a field and that field was missing. So I always assert on the used fields:

describe Car do
  context 'fields' do
    specify {expect(subject).to respond_to(:name)}
    specify {expect(subject).to respond_to(:filter)}
  end
end

Testing the associations:
I test the presence of the correct associations using the 'shoulda-matchers' gem.

describe Car do
  context 'assotiations' do
    specify { expect(subject).to belong_to(:user) }
    specify { expect(subject).to belong_to(:manufacturer) }
  end
end

Testing the validations:
I usually test the validations of the fields using the 'shoulda-matchers' gem.

describe Car do
  context 'validations' do
    specify { expect(subject).to validate_uniqueness_of(:filter).scoped_to(:manufacturer_id)}
  end
end

Testing the callbacks:
My callback test usually are like this:

describe Car do
  context 'validations' do
    context 'before_destroy' do
      specify 'call destroy like callback'do
        expect(equipment).to receive(:destroy_likes)
        equipment.destroy
      end
    end
  end
end

Testing the methods:
Usually I test the methods by calling it the asserting that all the necessary changes are made. I do this for all the execution paths.