Skip to main content

Have been doing some work with unit tests and finalizers. My understanding is that in general, finalizers get their own set of limits. However, in my testing, this is not the case in unit tests. In other words, if I create a limit exception (such as DML inserts) that ends with an exception, when the finalizer runs inside of a unit tests, it is unable to perform a DML operation as the DML limit has already been reached.

Is this expected behavior?

7 comments
  1. Oct 8, 2021, 7:26 PM

    Hi @Srikanth Ganapavarapu,

    thanks very much for responding. Here's my example.

     

    BTW, I'm really following up on @Daniel Appleman's first comment rather than the original post.

     

    Imagine a common logging framework storing in a custom object or something. This is intended to catch even Limit exceptions and make sure things are logged in a consistent way. Most exceptions can of course be caught, logged and re-thrown, but not the likes of CPU Time or Heap Size.

    public with sharing class FinalizerLogger implements Finalizer {

    public void execute(FinalizerContext context) {

    if (context.getResult() == ParentJobResult.UNHANDLED_EXCEPTION) {

    Id asyncJobId = context.getAsyncApexJobId();

    Exception exceptionThrown = context.getException();

    //logJobException(asyncJobId, exceptionThrown);

    System.debug(asyncJobId);

    System.debug(exceptionThrown);

    }

    }

    }

    Then there is the Unit Test. To test this behaviour I need to have a Queueable job that throws and unhandled exception. But when I do that, the test fails as a whole. Of course in real life the job also failed, so it's no different. I was just wondering how to structure the test properly and/or elegantly.

    I was trying to mock out or deserialise the FinalizerContext class to call execute directly, but that's not allowed.

    @IsTest

    private class FinalizerLoggerTest {

    private class AsyncClassWithFinalizer implements Queueable {

    public void execute(QueueableContext context) {

    System.attachFinalizer(new FinalizerLogger());

    Integer i = 1 / 0; //cause exception

    }

    }

    @IsTest

    static void testFinalizerLoger() {

    Id jobId = System.enqueueJob(new AsyncClassWithFinalizer());

    Test.startTest();

    // have Queueable execute by StopTest

    Test.stopTest();

    //assert logs

    }

    }

    The more I look into this the less on an issue it seems. The Class and test can be restructured to allow for things to be called explicitly in tests to ensure coverage. Not 100% real scenario, but we can trust that failed job will produce an exception in the Finalizer.

     

    I followed this up really out of curiosity and to make sure I've not missed something. So thanks very much for taking time to respond.

0/9000