Batch Apex is designed for handling large-scale operations that involve processing thousands or even millions of records—tasks that would normally exceed Salesforce’s standard execution limits. By dividing data into smaller sets, or batches, this approach allows processing to occur asynchronously, helping you stay within platform limits. If your use case involves actions like data cleanup, mass updates, or archiving, Batch Apex is often the most efficient choice.
When writing tests for Batch Apex, only a single execution of the execute method can be directly tested. To manage system resources effectively and avoid governor limit issues, you can specify the scope parameter in the executeBatch method, which controls how many records are processed at a time.
The executeBatch call runs asynchronously, meaning the process continues in the background after it starts. Therefore, during testing, it’s essential to ensure that the batch process completes before verifying the results. To do this, wrap the executeBatch call between the startTest and stopTest methods. Any asynchronous calls initiated after startTest are queued by Salesforce, and when stopTest runs, those queued operations are executed synchronously. If you omit startTest and stopTest, the batch job won’t execute until the very end of the test method in Apex versions 25.0 and later, and not at all in earlier versions.
From API version 22.0 onward, any exceptions that occur during a batch job invoked within a test are propagated back to the test method, causing the test to fail. To manage these exceptions properly, wrap your code in a try-catch block, placing the catch section after the stopTest method. In older versions (21.0 and earlier), such exceptions don’t bubble up to the test method, so they won’t cause it to fail.
global class ContactTypeUpdaterBatch implements Database.Batchable<SObject>, Database.Stateful {
Batch Class
// Query all Contacts that need updating
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator([
SELECT Id, Industry, Type
FROM Contact
WHERE Industry != null
]);
}
// Process each batch of records
global void execute(Database.BatchableContext bc, List<Contact> scope) {
List<Contact> contactsToUpdate = new List<Contact>();
for (Contact con : scope) {
String industryValue = con.Industry;
String currentType = con.Type;
// Build the new Type value
String newTypeValue = industryValue + ' Professional';
// Update if blank or not already prefixed correctly
if (currentType == null || currentType != newTypeValue) {
con.Type = newTypeValue;
contactsToUpdate.add(con);
}
}
if (!contactsToUpdate.isEmpty()) {
update contactsToUpdate;
}
}
// Finish method for any post-processing or logging
global void finish(Database.BatchableContext bc) {
System.debug('Contact Type update batch completed successfully.');
}
}
How to Run It
You can execute the batch from Anonymous Apex in the Developer Console or VS Code:
Explanation of the Test Class
Creating Test Data
We create four sample Contact records to cover different scenarios:
-
Contact 1: Has Industry but no Type → Should get “Technology Professional.”
-
Contact 2: Has Type “Professional” → Should be updated to “Finance Professional.”
-
Contact 3: Already correctly prefixed → Should remain unchanged.
-
Contact 4: Has no Industry → Should not be updated.
This ensures full code coverage and logical validation.
Running the Batch Inside a Test Context
-
Test.startTest()— Marks the beginning of the test’s execution phase. -
Database.executeBatch(batch, 200)— Runs the batch with a batch size of 200 records. -
Test.stopTest()— Forces all asynchronous operations (like Batch Apex) to finish before continuing.
Without startTest and stopTest, the batch might not complete before your assertions run.
Query Updated Records
After the batch runs, we query the same contacts to check if the Type field has been correctly updated based on the logic.
Assertions (System.assertEquals)
We verify that:
-
Contacts with a valid Industry have their
Typecorrectly prefixed. -
Contacts without Industry remain unchanged.
-
Contacts already matching the intended format are not altered.
These assertions confirm both functional correctness and that the batch’s conditional logic works as expected.
Code Coverage
This test class:
-
Covers 100% of the batch class code (all branches executed).
-
Validates all possible logical conditions.
-
Uses best testing practices — no hard-coded IDs, clear assertions, and test isolation.

Comments
Post a Comment