
Unit testing is a critical part of Salesforce development. It ensures your Apex code works as expected, meets business requirements, and adheres to Salesforce’s strict deployment standards. Writing high-quality test classes not only helps you catch bugs early but also ensures your code is robust, scalable, and maintainable.
In this blog, we’ll explore the importance of unit testing in Apex, best practices for writing test classes, and step-by-step examples to help you master the art of unit testing.
Table of Contents
ToggleWhy Is Unit Testing Important in Apex?
Salesforce requires at least 75% test coverage for all Apex code before it can be deployed to production. But beyond meeting this requirement, unit testing offers several benefits:
Bug Detection: Catch errors early in the development process.
Code Quality: Ensure your code meets functional and non-functional requirements.
Refactoring Confidence: Safely refactor code without breaking existing functionality.
Documentation: Serve as living documentation for how your code is supposed to work.
Key Concepts in Apex Unit Testing
Before diving into writing test classes, let’s review some key concepts:
Test Classes: Classes annotated with
@isTest
that contain test methods.Test Methods: Methods annotated with
@isTest
ortestMethod
that define individual test cases.Test Data: Data created specifically for testing, isolated from your org’s actual data.
Assertions: Statements that verify whether the code behaves as expected (e.g.,
System.assertEquals()
).
Best Practices for Writing High-Quality Test Classes
Here are the top best practices to follow when writing unit tests in Apex:
1. Use the @isTest Annotation
Always annotate your test classes and methods with @isTest
to ensure they are recognized as test code.
Example:
@isTest
public class MyTestClass {
@isTest
static void myTestMethod() {
// Test logic here
}
}
2. Create Test Data
Use the Test.startTest()
and Test.stopTest()
methods to isolate test data and governor limits. Create test data within the test class instead of relying on org data.
Example:
@isTest
public class AccountTest {
@isTest
static void testAccountCreation() {
// Create test data
Account acc = new Account(Name = 'Test Account');
insert acc;
// Start test context
Test.startTest();
// Call method to test
acc.Name = 'Updated Account';
update acc;
Test.stopTest();
// Verify results
Account updatedAcc = [SELECT Name FROM Account WHERE Id = :acc.Id];
System.assertEquals('Updated Account', updatedAcc.Name, 'Account name should be updated');
}
}
3. Use Assertions
Assertions are the backbone of unit testing. They verify that your code produces the expected results.
Common Assertion Methods:
System.assertEquals(expected, actual)
: Checks if two values are equal.System.assertNotEquals(unexpected, actual)
: Checks if two values are not equal.System.assert(condition)
: Checks if a condition is true.System.assertNotnull(variable)
: Checks if a variable is not null.
Example:
System.assertEquals(5, result, 'The result should be 5');
4. Test Bulk Operations
Ensure your code handles bulk data by testing with multiple records.
Example:
@isTest
public class BulkAccountTest {
@isTest
static void testBulkAccountCreation() {
List accounts = new List();
for (Integer i = 0; i < 200; i++) {
accounts.add(new Account(Name = 'Test Account ' + i));
}
Test.startTest();
insert accounts;
Test.stopTest();
List insertedAccounts = [SELECT Id FROM Account];
System.assertEquals(200, insertedAccounts.size(), '200 accounts should be created');
}
}
5. Test Positive and Negative Scenarios
Test both successful and error scenarios to ensure your code handles all cases.
Example:
@isTest
public class OpportunityTest {
@isTest
static void testOpportunityValidation() {
Opportunity opp = new Opportunity(Name = 'Test Opp', StageName = 'Prospecting', CloseDate = Date.today());
Test.startTest();
try {
insert opp;
System.assert(false, 'Expected validation error but none occurred');
} catch (DmlException e) {
System.assert(e.getMessage().contains('Required fields are missing'), 'Expected validation error for missing fields');
}
Test.stopTest();
}
}
6. Use Test.setMock for Callouts
When testing methods that make HTTP callouts, use Test.setMock
to simulate the callout response.
Example:
@isTest
public class CalloutTest {
@isTest
static void testCallout() {
Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());
Test.startTest();
String response = MyCalloutClass.makeCallout();
Test.stopTest();
System.assertEquals('Mock Response', response, 'Callout should return mock response');
}
}
public class MockHttpResponseGenerator implements HttpCalloutMock {
public HttpResponse respond(HttpRequest req) {
HttpResponse res = new HttpResponse();
res.setBody('Mock Response');
res.setStatusCode(200);
return res;
}
}
7. Achieve 100% Test Coverage
While Salesforce requires 75% coverage, aim for 100% to ensure all code paths are tested.
Real-World Example: Testing a Trigger
Let’s say you have a trigger that updates the account description when an opportunity is closed.
Trigger:
trigger OpportunityTrigger on Opportunity (after update) {
for (Opportunity opp : Trigger.new) {
if (opp.StageName == 'Closed Won') {
Account acc = [SELECT Id, Description FROM Account WHERE Id = :opp.AccountId];
acc.Description = 'Opportunity Closed Won: ' + opp.Name;
update acc;
}
}
}
Test Class:
@isTest
public class OpportunityTriggerTest {
@isTest
static void testOpportunityTrigger() {
Account acc = new Account(Name = 'Test Account');
insert acc;
Opportunity opp = new Opportunity(
Name = 'Test Opp',
StageName = 'Prospecting',
CloseDate = Date.today(),
AccountId = acc.Id
);
insert opp;
Test.startTest();
opp.StageName = 'Closed Won';
update opp;
Test.stopTest();
Account updatedAcc = [SELECT Description FROM Account WHERE Id = :acc.Id];
System.assertEquals('Opportunity Closed Won: Test Opp', updatedAcc.Description, 'Account description should be updated');
}
}
Conclusion
Unit testing in Apex is not just a deployment requirement—it’s a best practice that ensures your code is reliable, maintainable, and scalable. By following the best practices outlined in this blog, you can write high-quality test classes that catch bugs early, improve code quality, and provide confidence in your deployments.
Remember: A well-tested application is a reliable application. Invest time in writing thorough and meaningful tests to build robust Salesforce solutions.
-Jeet Singh