Skip to main content

Hi 

 

I'm confused in Apex Specialist. 

One of the requirements is to create a unit test WarehouseCalloutServiceTest. 

 

There is a condition: 

"Ensure that your code operates as expected in the scheduled context by validating that it executes after Test.stopTest() without exception. 

 Also assert that a scheduled asynchronous job is in the queue. The test classes for the callout service and scheduled test must also have 100% test coverage." 

 

So I must use `System.schedule` 

 

@isTestpublic class WarehouseCalloutServiceTest {

@isTest static public void warehouseCalloutServiceTest() {

Test.startTest();

Test.setMock(HttpCalloutMock.class, new WarehouseCalloutServiceMock());

// Schedule the test job

String jobId = System.schedule(

'WarehouseCalloutServiceTest',

'0 0 0 15 3 ? 2042',

new WarehouseSyncSchedule()

);

Assert.isNotNull(jobId, 'JOB ID must be not null.');

Test.stopTest();

// Now schedulled job is finished and I can test result via Assert

...

Assert...

 

Now let's look at the `WarehouseCalloutServiceTest` class 

 

public class WarehouseSyncSchedule implements Schedulable {

public void execute(SchedulableContext ctx) {

WarehouseCalloutService srv = new WarehouseCalloutService();

srv.updateProducts(); // there is a callout inside this method

}

}

the solution does not work, it throws an exception `System.CalloutException: Callout from scheduled Apex not supported`. 

So I must use asynchronous queue 

 

public void execute(SchedulableContext ctx) {

WarehouseCalloutService srv = new WarehouseCalloutService();

System.enqueueJob(srv);

}

 

public class WarehouseCalloutService implements Queueable, Database.AllowsCallouts {

public void execute(QueueableContext context) {

this.updateProducts(); // there is a callout inside this method

}

}

And now I have 100% test coverage, but any asserts do not pass. 

 

" All asynchronous calls made after the startTest method are collected by the system. When stopTest is executed, all asynchronous processes are run synchronously." 

 

So `WarehouseCalloutServiceTest.execute` will be ran at the moment of `Test.stopTest()` because `System.schedule` is wrapped in startTest/stopTest. 

`WarehouseCalloutService` will be enqueued but `WarehouseCalloutService.execute` will be never ran because `System.enqueueJob` is not wrapped in startTest/stopTest. 

 

So any Asserts will be passed. 

What is wrong? 

 

Regards, 

Alexander 

 

#Trailhead Superbadges

1 个回答
  1. 9月20日 13:48

    Hi Oleksandr, your understanding is close, but the important part is that the Queueable job enqueued from the scheduled Apex is a second level of asynchronous execution. 

     

    Test.stopTest() causes the scheduled job to execute, so your WarehouseSyncSchedule.execute() runs. When it calls System.enqueueJob(), the Queueable is placed in the async queue, but you shouldn't rely on that second-level async job executing within the same test context. 

     

    For the Apex Specialist requirement, you can assert that the scheduled job was created and that the scheduled execution completes without exception. You can also query AsyncApexJob before/after the appropriate point to verify the Queueable was enqueued.

    The production implementation should use:

    public void execute(SchedulableContext ctx) {

    System.enqueueJob(new WarehouseCalloutService());

    }

    and the Queueable should implement Database.AllowsCallouts.

    For the callout itself, use Test.setMock(HttpCalloutMock.class, ...) before Test.stopTest().

    So the key distinction is:

    Test.startTest() → System.schedule() → Test.stopTest() → Scheduled Apex executes → Queueable is enqueued

    You don't need to make the scheduled class perform the callout directly; the Queueable is the correct pattern for a callout from scheduled Apex. 

0/9000