How to Write Test Classes in Salesforce
📝 Summary
This article provides a comprehensive guide to writing test classes in Salesforce. Test classes ensure that your Apex code works as expected and meets Salesforce’s deployment requirements by achieving at least 75% code coverage.
⚙️ What Are Test Classes?
Test classes are written in Apex to verify that your triggers, classes, and other logic behave as expected. They simulate real-world scenarios without affecting actual data.
✅ Benefits of Writing Tests
- Required for deployment to production.
- Help catch bugs early.
- Validate business logic under various conditions.
- Ensure future code changes don’t break existing functionality (regression testing).
📌 Key Requirements
- Minimum 75% code coverage for deployment.
- All triggers must be tested.
- Positive and negative test cases should be included.
- Use System.assert() statements to validate behavior.
🛠 Basic Structure of a Test Class
@isTest
private class AccountHandlerTest {
@testSetup
static void setupData() {
Account acc = new Account(Name = 'Test Account');
insert acc;
}
@isTest
static void testAccountInsertLogic() {
Contact con = new Contact(FirstName = 'Test', LastName = 'User', AccountId = [SELECT Id FROM Account LIMIT 1].Id);
insert con;
// Validate behavior
Contact insertedCon = [SELECT Description FROM Contact WHERE Id = :con.Id];
System.assertEquals('Created by trigger', insertedCon.Description);
}
}
🔑 Best Practices
1. Use @isTest
Annotation
- Marks the class or method as a test.
- Test classes do not count against org Apex limits.
2. Use @testSetup
for Common Data
- Creates baseline data that is reused across tests.
- Runs once per test class, improving performance.
3. Test All Logic Paths
- Positive tests (valid inputs).
- Negative tests (invalid inputs, exceptions).
- Edge cases (empty fields, null values).
4. Avoid Using SeeAllData=true
- Tests should be isolated from org data.
- Create all necessary test data programmatically.
5. Use System.assert()
- Validates that actual results match expectations.
6. Cover Bulk Operations
- Simulate inserts/updates with up to 200 records.
7. Test Triggers and Exceptions
- Make sure you cover both success and failure paths.
💡 Common Patterns
Positive Test
@isTest
static void testValidInsert() {
Account acc = new Account(Name = ‘Test’);
insert acc;
System.assertNotEquals(null, acc.Id);
}
Negative Test (Expecting Failure)
@isTest
static void testInvalidInsert() {
try {
Account acc = new Account(); // Missing required Name
insert acc;
System.assert(false, ‘Insert should have failed’);
} catch (DmlException e) {
System.assert(e.getMessage().contains(‘Name’));
}
}
📊 Viewing Code Coverage
- Setup > Apex Test Execution
- Developer Console > Test > New Run
- Use Test.run() in Anonymous Apex to run test methods programmatically.
❌ Common Mistakes
Mistake | Impact | Fix |
---|---|---|
Not creating test data | Tests fail or behave unpredictably | Use @testSetup or method-level test data |
Not using System.assert() | Tests don’t verify logic | Always assert expected results |
Overusing SeeAllData=true | Makes tests dependent on real data | Create all required test data in the test class |
Ignoring exceptions | Can miss logic flaws | Include tests for invalid inputs and error conditions |
Only testing one record | Doesn’t cover bulk behavior | Simulate large data sets (up to 200 records) |
🧪 Example: Testing a Trigger
@isTest
private class ContactTriggerTest {
@isTest
static void testBeforeInsert() {
Contact c = new Contact(FirstName='John', LastName='Doe');
insert c;
Contact inserted = [SELECT Description FROM Contact WHERE Id = :c.Id];
System.assertEquals('Created by trigger', inserted.Description);
}
}