Search code examples
ruby-on-railsruby-on-rails-4rspecsingle-table-inheritance

rspec testing subclass sti methods rails 4


I am trying to build an rspec test for a method on an sti subclass and the test only reads the parent model's method. The method works in the app, just not in the rspec test. I can't figure out what I'm missing

models/animals/animal.rb

class Animal < ActiveRecord::Base

  def favorite
    "unicorn"
  end

end

models/animals/mammal_animal.rb

class MammalAnimal < Animal

  def favorite
    "whale"
  end

end

models/animals/cat_mammal_animal.rb

class CatMammalAnimal < MammalAnimal

  def favorite
    "tabby"
  end

end

mammal_animal_spec.rb

require 'rails_helper'

RSpec.describe MammalAnimal, type: :model do

  let(:cat_mammal_animal) {FactoryGirl.create(:cat_factory)}
  subject(:model) { cat_mammal_animal }
  let(:described_class){"MammalAnimal"}

  describe "a Cat" do
    it "should initialize successfully as an instance of the described class" do
      expect(subject).to be_a_kind_of described_class
    end

    it "should have attribute type" do
      expect(subject).to have_attribute :type
    end

    it "has a valid factory" do 
      expect(cat_mammal_animal).to be_valid
    end

    describe ".favorite " do 
      it 'shows the favorite Cat' do
        expect(cat_mammal_animal.type).to eq("CatMammalAnimal")
        expect(cat_mammal_animal.favorite).to include("tabby")
        expect(cat_mammal_animal.favorite).not_to include("whale")
        expect(cat_mammal_animal.favorite).not_to include("unicorn")
        print cat_mammal_animal.favorite
      end
    end
  end
end

error

Failures:
  1) MammalAnimal.favorite and .favorite shows the favorite Cat
     Failure/Error: expect(cat_mammal_animal.type).to include("tabby")
       expected "unicorn" to include "tabby"
     # ./spec/models/mammal_animal_spec.rb:82:in `block (3 levels) in <top (required)>'

UPDATE

animals.rb

FactoryGirl.define do
  factory :animal do
    type 'Animal'
    name "dragon"


    trait :mammal do
      type 'MammalAnimal'
      name "zebra"
    end

    trait :cat do
      type 'CatMammalAnimal'
      name "calico"
    end

    factory :mammal_factory, traits: [:mammal]
    factory :cat_factory, traits: [:cat]

  end

end

as per a suggestion, I added the below line to the test

expect(cat_mammal_animal.class.constantize).to eq(CatMammalAnimal)

and got this error

1) MammalAnimal.favorite and .favorite shows the favorite Cat Failure/Error: expect(cat_animal_mammal.class.constantize).to eq(CatMammalAnimal)

 NoMethodError:
   undefined method `constantize' for #<Class:0x007f8ed4b8b0e0>
   Did you mean?  constants

Solution

  • I think instead of using trait to create objects of subclasses, you should have separate factories for those too.

    factory :animal do
      name 'dragon'
    end
    
    factory :mammal, class: MammalAnimal do
      name 'zebra'
    end
    
    factory :cat, class: CatMammalAnimal do
      name 'calico'
    end
    

    All of these can be defined in animals.rb

    Then you can create your objects like

    create(:animal)
    create(:mammal)
    create(:cat)