Search code examples
rspecrails-activerecordrspec-railsruby-on-rails-5

How can I test methods in the ApplicationRecord abstract base class?


I haven't found a good way to test ApplicationRecord methods.

Let's say I have a simple method named one:

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  def one
    1
  end
end

And I want to test it:

describe ApplicationRecord do
  let(:it) { described_class.new }

  it 'works' do
    expect(it.one).to eq 1
  end
end

This dies, unsurprisingly, with NotImplementedError: ApplicationRecord is an abstract class and cannot be instantiated.

So I tried the anonymous class suggestion in Testing abstract classes in Rspec:

let(:it) { Class.new(described_class).new }

And this dies with TypeError: no implicit conversion of nil into String, presumably because the record's table name is nil.

Can anyone suggest a nice, simple way to test ApplicationRecord methods? Hopefully one that doesn't introduce dependencies on other classes in my application and doesn't root around in ActiveRecord internals?


Solution

  • This has been working for me in our tests:

    class TestClass < ApplicationRecord
      def self.load_schema!
        @columns_hash = {}
      end
    end
    
    describe ApplicationRecord do
      let(:record) { TestClass.new }
    
      describe "#saved_new_record?" do
        subject { record.saved_new_record? }
    
        before { allow(record).to receive(:saved_change_to_id?).and_return(id_changed) }
    
        context "saved_change_to_id? = true" do
          let(:id_changed) { true }
    
          it { is_expected.to be true }
        end
    
        context "saved_change_to_id? = false" do
          let(:id_changed) { false }
    
          it { is_expected.to be false }
        end
      end
    end
    

    It just prevents the class from attempting a database connection to load the table schema.

    Obviously as Rails moves along you might have to update the way you do this but at least it is located in one easy to find place.

    I prefer this much more than having another module just to allow testing.