mirror of
https://github.com/forcedotcom/afv-library.git
synced 2026-07-31 20:12:16 +08:00
166 lines
4.4 KiB
Markdown
166 lines
4.4 KiB
Markdown
# Assertion Patterns
|
|
|
|
## Assertion Methods
|
|
|
|
| Method | Use Case |
|
|
|--------|----------|
|
|
| `System.assertEquals(expected, actual, msg)` | Exact equality |
|
|
| `System.assertNotEquals(expected, actual, msg)` | Value should differ |
|
|
| `System.assert(condition, msg)` | Boolean condition |
|
|
|
|
**Always include the third parameter (message)** - Makes test failures meaningful.
|
|
|
|
## Good vs Bad Assertions
|
|
|
|
### ❌ Bad: No message, tests coverage not behavior
|
|
|
|
```apex
|
|
System.assertEquals(true, result);
|
|
System.assert(accounts.size() > 0);
|
|
```
|
|
|
|
### ✅ Good: Descriptive message, tests specific behavior
|
|
|
|
```apex
|
|
System.assertEquals(true, result, 'Service should return true for valid input');
|
|
System.assertEquals(200, accounts.size(), 'All 200 accounts should be processed');
|
|
```
|
|
|
|
## Common Assertion Patterns
|
|
|
|
### Collection Size
|
|
|
|
```apex
|
|
// Exact count
|
|
System.assertEquals(200, results.size(), 'Should process all 200 records');
|
|
|
|
// Not empty
|
|
System.assert(!results.isEmpty(), 'Results should not be empty');
|
|
|
|
// Empty
|
|
System.assert(results.isEmpty(), 'No results expected for invalid input');
|
|
```
|
|
|
|
### Field Values
|
|
|
|
```apex
|
|
// Single record
|
|
System.assertEquals('Processed', acc.Status__c, 'Account status should be updated to Processed');
|
|
|
|
// All records in collection
|
|
for (Account acc : updatedAccounts) {
|
|
System.assertEquals('Active', acc.Status__c,
|
|
'Account ' + acc.Name + ' should have Active status');
|
|
}
|
|
```
|
|
|
|
### Exception Testing
|
|
|
|
```apex
|
|
@isTest
|
|
static void shouldThrowException_WhenInputInvalid() {
|
|
Boolean exceptionThrown = false;
|
|
String exceptionMessage = '';
|
|
|
|
Test.startTest();
|
|
try {
|
|
MyService.process(null);
|
|
} catch (MyCustomException e) {
|
|
exceptionThrown = true;
|
|
exceptionMessage = e.getMessage();
|
|
}
|
|
Test.stopTest();
|
|
|
|
System.assert(exceptionThrown, 'MyCustomException should be thrown for null input');
|
|
System.assert(exceptionMessage.contains('cannot be null'),
|
|
'Exception message should mention null input');
|
|
}
|
|
```
|
|
|
|
### DML Results
|
|
|
|
```apex
|
|
// Insert success
|
|
Database.SaveResult[] results = Database.insert(accounts, false);
|
|
for (Database.SaveResult sr : results) {
|
|
System.assert(sr.isSuccess(), 'Insert should succeed: ' + sr.getErrors());
|
|
}
|
|
|
|
// Expected failures
|
|
Database.SaveResult sr = Database.insert(invalidAccount, false);
|
|
System.assert(!sr.isSuccess(), 'Insert should fail for invalid data');
|
|
System.assert(sr.getErrors()[0].getMessage().contains('REQUIRED_FIELD_MISSING'),
|
|
'Error should indicate missing required field');
|
|
```
|
|
|
|
### Comparing Objects
|
|
|
|
```apex
|
|
// Compare specific fields, not entire objects
|
|
System.assertEquals(expected.Name, actual.Name, 'Names should match');
|
|
System.assertEquals(expected.Status__c, actual.Status__c, 'Status should match');
|
|
|
|
// Or use JSON for deep comparison (use sparingly)
|
|
System.assertEquals(
|
|
JSON.serialize(expected),
|
|
JSON.serialize(actual),
|
|
'Objects should be identical'
|
|
);
|
|
```
|
|
|
|
### Date/DateTime Assertions
|
|
|
|
```apex
|
|
// Exact date
|
|
System.assertEquals(Date.today(), record.CreatedDate__c, 'Should be created today');
|
|
|
|
// Date within range
|
|
System.assert(record.DueDate__c >= Date.today(), 'Due date should be in the future');
|
|
System.assert(record.DueDate__c <= Date.today().addDays(30),
|
|
'Due date should be within 30 days');
|
|
```
|
|
|
|
### Null Checks
|
|
|
|
```apex
|
|
// Should be null
|
|
System.assertEquals(null, result.ErrorMessage__c, 'No error expected for valid input');
|
|
|
|
// Should not be null
|
|
System.assertNotEquals(null, result.Id, 'Record should have been inserted');
|
|
```
|
|
|
|
## Anti-Patterns to Avoid
|
|
|
|
### ❌ Testing implementation, not behavior
|
|
|
|
```apex
|
|
// Bad: Testing that a specific method was called
|
|
System.assert(MyClass.methodWasCalled, 'Method should be called');
|
|
|
|
// Good: Testing the observable outcome
|
|
System.assertEquals('Expected Value', record.Field__c, 'Field should be updated');
|
|
```
|
|
|
|
### ❌ Overly generic assertions
|
|
|
|
```apex
|
|
// Bad: Passes for any non-empty result
|
|
System.assert(results.size() > 0);
|
|
|
|
// Good: Verifies exact expected count
|
|
System.assertEquals(200, results.size(), 'All 200 records should be returned');
|
|
```
|
|
|
|
### ❌ Missing negative test assertions
|
|
|
|
```apex
|
|
// Bad: Only tests that no exception occurred
|
|
MyService.process(data); // Test passes if no exception
|
|
|
|
// Good: Verifies the actual outcome
|
|
Result r = MyService.process(data);
|
|
System.assertEquals('Success', r.status, 'Processing should succeed');
|
|
System.assertEquals(0, r.errorCount, 'No errors should occur');
|
|
```
|