after_commit executed despite use_transactional_fixtures = true

Skip to first unread message

Younes Serraj

Apr 26, 2023, 11:31:06 AM4/26/23
to rspec
Hello all,

I'm struggling to understand why an `after_create_commit` is executed in an example although `use_transactional_fixtures` is set to `true`.

I created a new Rails app to isolate/test this behavior:

ruby 3.1.2
rspec-rails 6.0.1

$ rails g scaffold article title content:text
$ rails g scaffold comment article:references content:text

Here are the models:

class Article < ApplicationRecord
  has_many :comments, dependent: :destroy

  after_create_commit -> { comments.create!(content: "First comment of #{title}") }

class Comment < ApplicationRecord
  belongs_to :article

Then I wrote the following test:

require 'rails_helper'

RSpec.describe Article, type: :model do
  describe "create" do
    subject { described_class.create!(title: "Hello", content: "world") }

    it 'does not create a first comment' do
      ActiveRecord::Base.logger =
      expect { subject }.not_to change(Comment, :count).by(1)

I expect it NOT to create a comment because, from my understanding:
- The example is run within a transaction
- The after_create_commit is supposed to be run only after the transaction is committed
- RSpec is supposed to rollback the transaction (once the example is over) instead of committing it
- No commit, no after_create_commit. No after_create_commit, not comment created.

To my surprise, the comment _is_ created. Here is the SQL log for the above example:

D, [2023-04-26T15:51:25.707540 #346392] DEBUG -- :   Comment Count (0.0ms)  SELECT COUNT(*) FROM "comments"
D, [2023-04-26T15:51:25.709992 #346392] DEBUG -- :   TRANSACTION (0.0ms)  SAVEPOINT active_record_1
D, [2023-04-26T15:51:25.710339 #346392] DEBUG -- :   Article Create (0.1ms)  INSERT INTO "articles" ("title", "content", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["title", "Hello"], ["content", "world"], ["created_at", "2023-04-26 13:51:25.709772"], ["updated_at", "2023-04-26 13:51:25.709772"]]
D, [2023-04-26T15:51:25.710480 #346392] DEBUG -- :   TRANSACTION (0.0ms)  RELEASE SAVEPOINT active_record_1
D, [2023-04-26T15:51:25.713710 #346392] DEBUG -- :   TRANSACTION (0.0ms)  SAVEPOINT active_record_1
D, [2023-04-26T15:51:25.713915 #346392] DEBUG -- :   Comment Create (0.0ms)  INSERT INTO "comments" ("article_id", "content", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["article_id", 1], ["content", "First comment of Hello"], ["created_at", "2023-04-26 13:51:25.713563"], ["updated_at", "2023-04-26 13:51:25.713563"]]
D, [2023-04-26T15:51:25.714024 #346392] DEBUG -- :   TRANSACTION (0.0ms)  RELEASE SAVEPOINT active_record_1
D, [2023-04-26T15:51:25.714173 #346392] DEBUG -- :   Comment Count (0.0ms)  SELECT COUNT(*) FROM "comments"
D, [2023-04-26T15:51:25.725359 #346392] DEBUG -- :   TRANSACTION (0.1ms)  rollback transaction

It seems that the after_create_commit block is run after the savepoint, which does not have the same meaning in my mind as after a commit. A savepoint can still be rolled back so I'm not yet sure the data is effectively persisted in the database while a commit cannot be rolled back and the data _is_ persisted.

Am I missing something here? Any explanation/help will be much appreciated!


Jon Rowe

Apr 26, 2023, 11:45:39 AM4/26/23
to rspec

Although this functionality is configured via `rspec-rails` config, its part of the thin wrapper for Rails functionality, so all RSpec does here is set the Rails setting.

That setting is supposed to wrap the entire test/spec in a transaction before rolling it back, and you can see the rollback at the end of the transaction, however you can see (presumedly) Rails automatically triggering the hook from the savepoint it presumedly created for you, thus creating the comments.

The end result of this entire thing should be nothing left in the database, but within the spec the transaction has yet to be rolled back, so the spec would fail.

Reply all
Reply to author
0 new messages