Skip to main content

SF-0414 · Coding · Medium

Can you write the sample code for a queueable class?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

A minimal Queueable is six lines, but interviewers want to see you cover the patterns that come up in real projects: typed constructor input, optional callouts, and a test class with Test.startTest() and Test.stopTest() around the enqueue.

Production-shaped Queueable

public with sharing class AccountSyncQueueable implements Queueable {

    private final List<Account> accountsToSync;

    public AccountSyncQueueable(List<Account> accountsToSync) {
        this.accountsToSync = accountsToSync;
    }

    public void execute(QueueableContext qc) {
        // Do some work — for example, stamp a sync timestamp
        for (Account a : accountsToSync) {
            a.Last_Synced__c = System.now();
        }
        update accountsToSync;

        // Chain the next job if there's more to do
        List<Account> remaining = [
            SELECT Id
            FROM Account
            WHERE Last_Synced__c = null
            LIMIT 100
        ];
        if (!remaining.isEmpty() && !Test.isRunningTest()) {
            System.enqueueJob(new AccountSyncQueueable(remaining));
        }
    }
}

What the interviewer is looking for in this snippet:

  • Typed constructorList<Account> (not a list of Ids you re-query inside execute()).
  • final field — clear that the input is set at construction and not mutated by the framework.
  • Chaining with System.enqueueJob to continue the work when more records remain.
  • Test guard on chaining so tests don’t recurse.

Submitting the job

List<Account> firstBatch = [
    SELECT Id, Last_Synced__c
    FROM Account
    WHERE Last_Synced__c = null
    LIMIT 100
];
Id jobId = System.enqueueJob(new AccountSyncQueueable(firstBatch));
System.debug('Enqueued job: ' + jobId);

System.enqueueJob returns the AsyncApexJob.Id — store it if you need to poll status from another transaction.

Queueable with callouts

Add the Database.AllowsCallouts marker:

public class WebhookQueueable implements Queueable, Database.AllowsCallouts {
    private final Id recordId;
    public WebhookQueueable(Id recordId) { this.recordId = recordId; }

    public void execute(QueueableContext qc) {
        HttpRequest req = new HttpRequest();
        req.setEndpoint('callout:Webhook/notify');
        req.setMethod('POST');
        req.setBody(JSON.serialize(new Map<String, Id>{ 'id' => recordId }));
        HttpResponse res = new Http().send(req);
        System.debug(res.getStatus());
    }
}

Test class

@isTest
private class AccountSyncQueueableTest {

    @isTest
    static void syncsAccounts() {
        List<Account> accounts = new List<Account>();
        for (Integer i = 0; i < 5; i++) {
            accounts.add(new Account(Name = 'Test ' + i));
        }
        insert accounts;

        Test.startTest();
        System.enqueueJob(new AccountSyncQueueable(accounts));
        Test.stopTest();

        for (Account a : [SELECT Last_Synced__c FROM Account]) {
            System.assertNotEquals(null, a.Last_Synced__c, 'Should have been synced');
        }
    }
}

Test.stopTest() forces the queued job to run synchronously, so the asserts after it inspect the real result.

Common follow-ups

  • Why not @future? — Typed inputs, chaining, and a real job Id. See the Queueable interface question for the full comparison.
  • Chain limit? — 5 levels deep in production, unlimited in tests.
  • What’s QueueableContext for?qc.getJobId() gives you the current AsyncApexJob Id (useful for logging).

Verified against: Apex Developer Guide — Queueable Apex. Last reviewed 2026-05-17 for Spring ‘26.