RSpec, use_transaction_fixtures and after_commit

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 …


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:


This content originally appeared on DEV Community and was authored by John Carneiro


Print Share Comment Cite Upload Translate Updates
APA

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/

MLA
" » RSpec, use_transaction_fixtures and after_commit." John Carneiro | Sciencx - Wednesday August 18, 2021, https://www.scien.cx/2021/08/18/rspec-use_transaction_fixtures-and-after_commit/
HARVARD
John Carneiro | Sciencx Wednesday August 18, 2021 » RSpec, use_transaction_fixtures and after_commit., viewed ,<https://www.scien.cx/2021/08/18/rspec-use_transaction_fixtures-and-after_commit/>
VANCOUVER
John Carneiro | Sciencx - » RSpec, use_transaction_fixtures and after_commit. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/08/18/rspec-use_transaction_fixtures-and-after_commit/
CHICAGO
" » RSpec, use_transaction_fixtures and after_commit." John Carneiro | Sciencx - Accessed . https://www.scien.cx/2021/08/18/rspec-use_transaction_fixtures-and-after_commit/
IEEE
" » RSpec, use_transaction_fixtures and after_commit." John Carneiro | Sciencx [Online]. Available: https://www.scien.cx/2021/08/18/rspec-use_transaction_fixtures-and-after_commit/. [Accessed: ]
rf:citation
» RSpec, use_transaction_fixtures and after_commit | John Carneiro | Sciencx | 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.

You must be logged in to translate posts. Please log in or register.