I've been dancing around this for a little bit, and can't seem to figure it out.
For reference: Testing after_commit with RSpec and mocking http://www.chrisrolle.com/en/blog/activerecord-callback-tests-with-rspec
My code:
# model
class DataSource < ApplicationRecord
after_commit :subscribe, on: %i[create update], if: :url_source?
...
end
# spec file
require 'rails_helper'
require 'sidekiq/testing' #include in your Rspec file
Sidekiq::Testing.fake! #include in your RSpec file
RSpec.describe DataSource, type: :model do
describe 'When creating, updating or destroying feed sources' do
let(:create_data_source) { FactoryBot.build(:data_source) }
it 'should call subscribe when created' do
create_data_source.run_callbacks(:create)
expect(create_data_source).to receive(:subscribe)
end
FactoryBot.build
creates a .new
instance of the object in question. So, like Christian Rolle's post, I've got a .new
thing that isn't yet saved. I'm assuming that run_callbacks(:create)
is actually doing the create.
Looking in the test log, that appears to be the case:
DataSource Create (0.2ms) INSERT INTO "data_sources" ("name", "slug", "url", "data", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["name", "Andreas Johnston"], ["slug", "andreas-johnston"], ["url", "http://kunze.biz/titus"], ["data", "{\"feed_type\":\"rss\"}"], ["created_at", "2021-12-02 19:54:32.967931"], ["updated_at", "2021-12-02 19:54:32.967931"]]
If I insert a binding.pry
into the subscribe
method on the DataSource
model and execute the test, I do end up inside the subscribe
method, so that is also being called.
However, rspec reports the following error:
(#<DataSource id: nil, name: "Gov. Rachelle Ernser", slug: nil, url: "http://goldner.info/shakia", data: {"feed_type"=>"rss"}, created_at: nil, updated_at: nil, abstracted_source: false, status: nil>).subscribe(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
If I change the test to the following:
let(:create_data_source) { FactoryBot.create(:data_source) }
it 'should call subscribe when created' do
expect(create_data_source).to receive(:subscribe)
end
I get the same error, except it shows that the object was already created at the time rspec was looking (created_at
has a value in the output):
Failure/Error: expect(create_data_source).to receive(:subscribe)
(#<DataSource id: 3, name: "Jesusita Kuhic", slug: "jesusita-kuhic", url: "http://nitzsche-gutkowski.io/cory.prosacco", data: {"feed_type"=>"rss"}, created_at: "2021-12-02 19:57:08", updated_at: "2021-12-02 19:57:08", abstracted_source: false, status: nil>).subscribe(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
It was recommended in another thread somewhere to use shoulda-callback-matchers
. Unfortunately, there is a bug with it and Rails 5.2 and Rails 6 that breaks my particular case:
https://github.com/jdliss/shoulda-callback-matchers/issues/26
The project has not been updated since 2016, and a fix has been outstanding for some time but it has not been accepted.
I'm not really sure how else to test that the subscribe
method is being called when the object is created. Update seems to work just fine:
it 'should call subscribe when updated' do
expect(data_source).to receive(:subscribe)
data_source.save
end
Calling .save
on a previously un-saved object also seems to work:
it 'should call subscribe when created' do
ds = FactoryBot.build(:data_source)
expect(ds).to receive(:subscribe)
ds.save
end
But I'm not sure if it's acceptable/safe/good practice to test create
via new
+save
.