Home > Mobile >  Rspec does not execute a "before_destroy" callback
Rspec does not execute a "before_destroy" callback

Time:08-15

In my book model, I have a "before_destroy" callback (with a potentially ugly method) as follows:

  before_destroy :destroy_fallback
  private

  def destroy_fallback
    unless self.fallback?
      format_fallback = BookFormat.find_by(fallback: true)
      Book.where(book_format_id: self.id).update(book_format_id: format_fallback.id)
    else
      errors.add(:base, :undestroyable)
      throw :abort
    end
  end

However when I test that this really happens, it doesn't seem to. This spec here results in an error (saying that the two id's are not the same): require 'rails_helper'

RSpec.describe BookFormat, type: :model do
  before(:all) do
    @book = create(:hobbit)
    @book_format_default = create(:not_defined)
  end

  it 'should reassign to the fallback book_format if their book_format is deleted' do
    format = @book.book_format
    format.destroy
    expect(@book.book_format.id).to eq(@book_format_default.id)
  end
end

Looks like the destroy_fallback is never executed, i.e. the before_destroy callback is not used? In dev, when I do this via website - everything seems to works as desired.

CodePudding user response:

Might just be copy-paste error, but you don't seem to have a destroy_fallback method in your model.

Make sure you're actually defining a method:

class Book < ApplicationRecord
 before_destroy :destroy_fallback

  ...

  private
  
  def destroy_fallback
    unless self.fallback?
      format_fallback = BookFormat.find_by(fallback: true)
      Book.where(book_format_id: self.id).update(book_format_id: format_fallback.id)
    else
      errors.add(:base, :undestroyable)
      throw :abort
    end
  end
end

And, I find unless - else to be really hard to reason about.

class Book < ApplicationRecord
 before_destroy :destroy_fallback

  ...

  private
  
  def destroy_fallback
    if self.fallback?
      errors.add(:base, :undestroyable)
      throw :abort
    else
      format_fallback = BookFormat.find_by(fallback: true)
      Book.where(book_format_id: self.id).update(book_format_id: format_fallback.id)
    end
  end
end

CodePudding user response:

(upon request promoting my comment to an answer)

Since your destroy callback is being called on the web request, the problem lies in the Rspec code...

You need to reload the @book model in the expect statement, so:

expect(@book.reload.book_format.id).to... etc.

  • Related