This content originally appeared on DEV Community and was authored by John Carneiro
Hello everyone, this is my first post in English, so sorry for my probable mistakes, I want to use this post to share some knowledge and practice my writing. Thank you for reading my post.
Firstly, some information about our project
In our project we use RSpec (also FactoryBot) with use_transaction_fixtures activated (see more details). This means that every example runs within a transaction, and then it removes that data by simply rolling back the transaction at the end of the example.
When we need to test after_commit
callback we execute the method object.run_callbacks(:commit)
after the operation (create, update or destroy).
What did I want to do?
I was writing a test for my model and there were three callbacks of type after_commit, one for each transaction type (create, update and destroy). I wanted to ensure that the system would generate the information correctly.
Now, imagine you create one record, then you update the same record and after you delete the record.
Below is an example of the code.
it 'notify service at every change' do
total_jobs = NotifyWorker.jobs.count
expect(total_jobs).to be_zero
person = create(:person)
person.run_callbacks(:commit)
expect(NotifyWorker.jobs.count).to eq(total_jobs + 1)
expect(NotifyWorker.jobs[0]['args']).to eq(
[
'create',
{
'id' => person.id,
'name' => person.name
}
]
)
person.update(name: 'test')
person.run_callbacks(:commit)
expect(NotifyWorker.jobs.count).to eq(total_jobs + 2)
expect(NotifyWorker.jobs[1]['args']).to eq(
[
'update',
{
'id' => person.id,
'name' => person.name
}
]
)
person.destroy
person.run_callbacks(:commit)
expect(NotifyWorker.jobs.count).to eq(total_jobs + 3)
expect(NotifyWorker.jobs[2]['args']).to eq(
[
'destroy',
{
'id' => person.id
}
]
)
end
I expected the app to create three jobs in sequence, one for each transaction type, but that's not what happened.
In this case of the update it genetared a job of type "create" and when I deleted the record, it generated a job correctly, so the problem was in the update, but I didn't understand why.
I searched on google some information that might help and found a question on StackOverflow about the same problem I had.
After create the record (person = create(:person)
), a instance of @_start_transaction_state
is initialized, but never cleared. See more details
This variable is used to control which is the action in transaction.
So, I just had to clear the variable, for that you can use the clear_transaction_record_state
method before executing after_commit
, but this is a protected method, so you should use the send
method, like that.
object.send(:clear_transaction_record_state)
Below you can see the final result.
it 'notify service at every change' do
total_jobs = NotifyWorker.jobs.count
expect(total_jobs).to be_zero
person = create(:person)
person.run_callbacks(:commit)
expect(NotifyWorker.jobs.count).to eq(total_jobs + 1)
expect(NotifyWorker.jobs[0]['args']).to eq(
[
'create',
{
'id' => person.id,
'name' => person.name
}
]
)
person.send(:clear_transaction_record_state)
person.update(name: 'test')
person.run_callbacks(:commit)
expect(NotifyWorker.jobs.count).to eq(total_jobs + 2)
expect(NotifyWorker.jobs[1]['args']).to eq(
[
'update',
{
'id' => person.id,
'name' => person.name
}
]
)
person.send(:clear_transaction_record_state)
person.destroy
person.run_callbacks(:commit)
expect(NotifyWorker.jobs.count).to eq(total_jobs + 3)
expect(NotifyWorker.jobs[2]['args']).to eq(
[
'destroy',
{
'id' => person.id
}
]
)
end
Again thank you for reading my post and I see you later.
References:
- https://relishapp.com/rspec/rspec-rails/v/5-0/docs/transactions
- https://apidock.com/rails/ActiveRecord/Transactions/clear_transaction_record_state
- https://stackoverflow.com/questions/33940268/after-commit-callback-on-update-doesnt-trigger
- https://apidock.com/rails/v4.0.2/ActiveRecord/Transactions/clear_transaction_record_state
- https://github.com/rails/rails/blob/4-0-stable/activerecord/lib/active_record/transactions.rb#L384
- https://github.com/rails/rails/blob/main/activerecord/lib/active_record/transactions.rb
This content originally appeared on DEV Community and was authored by John Carneiro
John Carneiro | Sciencx (2021-08-18T03:43:38+00:00) RSpec, use_transaction_fixtures and after_commit. Retrieved from https://www.scien.cx/2021/08/18/rspec-use_transaction_fixtures-and-after_commit/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.