4

I have a job, which should discard a specific error:

class SomeJob < ApplicationJob
discard_on(Errno::ENOENT) do |job, error|
    ...do things...
   end

   def perform(**args)
     ...do things...
   end
end

I have tried, based on How to properly test ActiveJob's retry_on method with rspec? to mock test it like that:

context '#discard_on' do
      it 'should discard on Errno::ENOENT error' do
        expect_any_instance_of(described_class).to receive(:perform).and_raise(Errno::ENOENT.new)
        expect(SomeJob).to have_received(:discard_on)

        UploadJob.perform_now(**args)
      end
    end

but I am keep getting the following error:

 #<SomeJob (class)> expected to have received discard_on, but that object is not a spy or method has not been stubbed.

If I try it like that:

expect_any_instance_of(SomeJob).to receive(:perform).and_raise(Errno::ENOENT)
expect_any_instance_of(described_class).to receive(:discard_on)

SomeJob.perform_now(**args)

It fails with this error:

SomeJob does not implement #discard_on
Assaf Sapir
  • 154
  • 2
  • 13
  • 2
    [`discard_on`](https://edgeapi.rubyonrails.org/classes/ActiveJob/Exceptions/ClassMethods.html) is a class method. And this just won't work at all since its not actually called when an exception is raised. Its just a macro method that uses `rescue_from` to setup a exception handler. I would recommend that you look into a different approach where you test the behavior instead of poking into the internals. There is a reason why `any_instance_of` is considered a really bad code smell. – max Apr 06 '20 at 06:43
  • Thanks @max. So I should maybe test that the thing that happen inside the block should have called? Also, why `any_instance_of` is a code smell? – Assaf Sapir Apr 06 '20 at 06:47
  • 1
    Its explained [in the docs](https://relishapp.com/rspec/rspec-mocks/docs/working-with-legacy-code/any-instance). – max Apr 06 '20 at 06:48
  • 1
    And yes. Test what the code does - like for example not creating a record or logging an error - not how it does it. – max Apr 06 '20 at 06:50
  • 2
    If perform is calling other methods, you can possibly mock one of them to raise an error, and then assert nothing raised on the perform. But I totally agree with @max , it's kinda testing that Rails is doing what is should be doing...you're not gaining a lot. – Joel Blum Apr 06 '20 at 08:00

0 Answers0