I'm confused in Apex Specialist and do not understand how to test scheduled job that uses a callout.
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
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.