TransWikia.com

Test a Trigger with Queues

Salesforce Asked on November 13, 2021

I am trying to write a test for my trigger that will queue up some job, unfortunately according to Salesforce, tests can’t have chain-able queues.

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_queueing_jobs.htm

You can’t chain queueable jobs in an Apex test. Doing so results in an
error. To avoid getting an error, you can check if Apex is running in
test context by calling Test.isRunningTest() before chaining jobs.

So when I run my test I get this error:

System.AsyncException: Maximum stack depth has been reached.

I need to find some way to get around this.

One way I was thinking is to let the trigger know that it’s a test, so that it won’t queue up the job. Here is my current code:

My Trigger class:

trigger MyTrigger on MyObj(after insert) {
    String CRON_EXP = '0 0 0 ? * * *';
    MyObjScheduler schedule =  new MyObjScheduler ();
    String jobId = System.schedule('MyTrigger', CRON_EXP, schedule);
}

My Scheduler Class:

global class MyObjScheduler implements Schedulable {
    global void execute(SchedulableContext ctx) {
        // Other business logic
        MyObjQueueableJob curJob = new MyObjQueueableJob ();
        System.enqueueJob(curJob); 
    }
}

My Queueable

public class MyObjQueueableJob implements Queueable {
    public void execute(QueueableContext context) {
        // Other business logic
        System.enqueueJob(otherJobs); 
    } }

If there is a way to communicate with Trigger, then I can just write it like this:

trigger MyTrigger on MyObj(after insert) {
    String CRON_EXP = '0 0 0 ? * * *';
    Boolean IsTest = false; // How to change this to true?
    MyObjScheduler schedule =  new MyObjScheduler ();
    if (!IsTest) {
        String jobId = System.schedule('MyTrigger', CRON_EXP, schedule);
    }
}

Is this possible?

2 Answers

Using if(!Test.isRunningTest()) allows you to purposefully execute (or not execute) code if you're running in a test context.

That can help you out of some simple situations, but a consequence of not running particular bits of code is that the code that does not get run during a test also does not get code coverage from that test.

You're going to need to cause your code to run at some point, and chained queueable jobs are likely still going to present an issue.

Option one

One potential answer to that is to stick to only writing unit tests (as opposed to, say, integration tests). The test only stresses one class, and any method from any other class that gets called needs to be stubbed/mocked.

This approach requires extra setup if you're using the Apex Mocks framework or The Stub API.

It may also mean that you need to design and architect your code in such a way that you can perform dependency injection.

Option two

Another option is to use Test.isRunningTest() to determine when you should enqueue your queueable job, and when you should just call the execute() method like it were any other method.

It may not be obvious at first, but there really is nothing stopping you from doing the following:

MyObjQueueableJob asyncWork = new MyObjQueueableJob();
asyncWork.execute(null);

The QueueableContext parameter can just have null passed with it. QueueableContext is an interface that provides one method, getJobId(), and that information doesn't really have much in the way of uses.

The advantage of this option is that you still run your queueable code in your test, and you can work around the chaining restriction.

The drawback of this approach is that everything you're running (in the test) is no longer async, and you won't get a fresh set of limits like you would outside of a test context. Running into governor limits may be a very real concern here.

One last note

Using Test.isRunningTest() to alter the path of execution in test contexts likely means that you won't be able to attain 100% coverage.

If you ask me though, 100% coverage is a nice thing to strive for but not really all that important. It's far more important that you write many test methods to cover a wide range of possible code paths (which naturally leads to high coverage) and make a ton of assertions (to validate that running your code ends up doing what you think it should be doing).

Answered by Derek F on November 13, 2021

If I understood your question correctly, in your trigger, you can write something like this:

String jobId;    
if(!Test.isRunningTest()){
    jobId  = System.schedule('MyTrigger', CRON_EXP, schedule);
}

Answered by Shailesh Patil on November 13, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP