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?
ActiveRecord::Base indicates that the ActiveRecord class or module has a static inner class called Base that you're extending.
Abstract Base Classes An abstract base class in Rails Model is simply a model that is not persistent, i.e not backed by a table. It would look like: # app/models/citizen.rbclass Citizen < ApplicationRecord. self.abstract_class = true. end.
An abstract class is created with all of the methods you'd expect to find on ApplicationRecord — validators, persistence methods, etc — but an instance of an abstract class can never exist on its own.
ApplicationRecord inherits from ActiveRecord::Base , which defines a number of helpful methods. You can use the ActiveRecord::Base.table_name= method to specify the table name that should be used: class Product < ApplicationRecord self. table_name = "my_products" end Copy.
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.
I would suggest extracting those methods into module (concern) and leave ApplicationRecord alone.
module SomeCommonModelMethods
extend ActiveSupport::Concern
def one
1
end
end
class ApplicationRecord < ActiveRecord::Base
include SomeCommonModelMethods
self.abstract_class = true
end
describe SomeCommonModelMethods do
let(:it) { Class.new { include SomeCommonModelMethods }.new } }
it 'works' do
expect(it.one).to eq 1
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With