dimanche 21 février 2021

Inside an RSpec matcher, how to make an instance available that gets created inside a model callback

I try to test a model. After the model under test is created, it creates an object from another model and sends out an email based on the other object's ID.

# model under test

class AccountabilityBuddy < ApplicationRecord
  after_save_commit :send_consent_inquiry

  def send_consent_inquiry
    consent_tracker = BuddyConsent.create!(accountability_buddy_id: self.id)
    BuddyMailer.with(buddy: self, consent_link: consent_tracker.id).buddy_request.deliver_later
  end
...

This is what I've tried so far:

# the model test

RSpec.describe AccountabilityBuddy, type: :model do
  subject(:buddy) { build(:accountability_buddy) }
  
    describe 'after_save callback' do
      let(:saving) do
        buddy.save!
        buddy.reload
      end

      it 'sends an email to the buddy' do
        # how that works: https://stackoverflow.com/questions/22988968/testing-after-commit-with-rspec-and-mocking/30901628#comment43748375_22989816
        expect { saving }
          .to have_enqueued_job(ActionMailer::MailDeliveryJob)
                .with('BuddyMailer', 'buddy_request', 'deliver_now', params: {buddy: buddy, consent_link: BuddyConsent.last.id }, args: [])
        # Test fails, BuddyConsent.last.id is nil
      end

      it 'creates a new BuddyConsent' do
        expect { saving }.to(change { BuddyConsent.all.count }.from(0).to(1))
        # Test passes
      end
    end
...

The first test fails because the BuddyConsent.last.id is nil. I know that BuddyConsent is generated correctly, because the ID is available inside the let block:

      let(:saving) do
        buddy.save!
        buddy.reload
        BuddyConsent.last.id # => "8be42055-112c-4ba6-bd1b-61b73946fb6e"
      end

How do I make it available inside the matcher and why is it out of context?

Aucun commentaire:

Enregistrer un commentaire