According to Salesforce docs:
All asynchronous calls made after the startTest method are collected
by the system. When stopTest is executed, all asynchronous processes
are run synchronously.
Now I was assuming this holds true for all async apex, but for the queueable apex, it executes even if we don't enqueue a job between start test and stop test.
Minimum Viable Code to reproduce:
Queueable Apex :
public class MyQueueableApex implements Queueable{
public void execute(QueueableContext context) {
System.debug('Inside Queueable apex');
}
}
Test class:
@isTest
public class MyQueueableApexTest {
@isTest
public static void queuableTest(){
Id jobId = System.enqueueJob(new MyQueueableApex());
System.assertNotEquals('Completed' , [SELECT Id , Status FROM AsyncApexJob WHERE Id=:jobId][0].Status);
}
}
Debug:
48.0 APEX_CODE,DEBUG;APEX_PROFILING,INFO;CALLOUT,INFO;DB,INFO;NBA,INFO;SYSTEM,DEBUG;VALIDATION,INFO;VISUALFORCE,INFO;WAVE,INFO;WORKFLOW,INFO
16:24:33.0 (404217)|USER_INFO|[EXTERNAL]|00528000000PIGY|pjxdsfse@gmail.com|(GMT+00:00) Greenwich Mean Time (Europe/London)|GMTZ
16:24:33.0 (445053)|EXECUTION_STARTED
16:24:33.0 (450671)|CODE_UNIT_STARTED|[EXTERNAL]|01p0I00000GAtQS|MyQueueableApexTest.queuableTest()
16:24:33.0 (11522220)|SOQL_EXECUTE_BEGIN|[8]|Aggregations:0|SELECT Id, Status FROM AsyncApexJob WHERE Id = :tmpVar1
16:24:33.0 (15845130)|SOQL_EXECUTE_END|[8]|Rows:1
16:24:33.16 (16115510)|CUMULATIVE_LIMIT_USAGE
16:24:33.16 (16115510)|LIMIT_USAGE_FOR_NS|(default)|
Number of SOQL queries: 1 out of 100
Number of query rows: 1 out of 50000
Number of SOSL queries: 0 out of 20
Number of DML statements: 0 out of 150
Number of DML rows: 0 out of 10000
Maximum CPU time: 0 out of 10000
Maximum heap size: 0 out of 6000000
Number of callouts: 0 out of 100
Number of Email Invocations: 0 out of 10
Number of future calls: 0 out of 50
Number of queueable jobs added to the queue: 1 out of 50
Number of Mobile Apex push calls: 0 out of 10
16:24:33.16 (16115510)|CUMULATIVE_LIMIT_USAGE_END
16:24:33.0 (16157837)|CODE_UNIT_FINISHED|MyQueueableApexTest.queuableTest()
16:24:33.0 (17551568)|EXECUTION_FINISHED
16:24:33.27 (27032903)|USER_INFO|[EXTERNAL]|00528000000PIGY|pjxdsfse@gmail.com|(GMT+00:00) Greenwich Mean Time (Europe/London)|GMTZ
16:24:33.27 (27047994)|EXECUTION_STARTED
16:24:33.27 (27051597)|CODE_UNIT_STARTED|[EXTERNAL]|01p0I00000GAtQN|MyQueueableApex
16:24:33.27 (28544098)|USER_DEBUG|[3]|DEBUG|Inside Queueable apex
16:24:33.27 (28634595)|CODE_UNIT_FINISHED|MyQueueableApex
16:24:33.27 (29986879)|EXECUTION_FINISHED
The status of async-apex job is still Pending, I can see from debug log. execute method was called even if I didn't use start , stop test at all. Is it expected behaviour documented somewhere?
The issue I faced is, I was unit-testing if queueable is being queued with dummy value, I didn't care about if it was successful or not, but my unit test broke cause Apex Test execute queueabale, and due to dummy values, SOQL failed.
Also this document states:
The ID of a queueable Apex job isn’t returned in test
context—System.enqueueJob returns null in a running test.
I could still see enqueueJob returning proper job id and not null.
Best Answer
I wrote a unit test that describes how all asynchronous code runs at the end of a unit test. This was in 2018, and still not fixed (and likely won't be).
The documentation is also incorrect, as far as I can tell (System.enqueueJob returning an ID in test context). Report a bug on Twitter to @salesforcedocs. They'll put something in for you.
If you do not call Test.stopTest, but you still want to kill any potential asynchronous jobs, use Database.rollback.
Doing this will roll back all parts of the transaction, including calling future methods, queueable methods, scheduled methods, batchable methods, emails, post-commit platform events, etc.