afv-library/skills/generating-apex-test/references/assertion-patterns.md
2026-03-31 00:51:15 -05:00

3.3 KiB

Assertion Patterns

Assertion Methods

Method Use Case
Assert.areEqual(expected, actual, msg) Exact equality
Assert.areNotEqual(expected, actual, msg) Value should differ
Assert.isTrue(condition, msg) Boolean condition
Assert.isFalse(condition, msg) Negated boolean condition
Assert.fail(msg) Force failure (e.g., expected exception not thrown)
Assert.isNotNull(value, msg) Non-null check
Assert.isNull(value, msg) Null check

Always include the message parameter — makes test failures actionable.

Good vs Bad Assertions

Bad: No message, tests coverage not behavior

Assert.isTrue(result); // no message
Assert.isTrue(accounts.size() > 0); // vague — use areEqual with exact count

Good: Descriptive message, tests specific behavior

Assert.isTrue(result, 'Service should return true for valid input');
Assert.areEqual(200, accounts.size(), 'All 200 accounts should be processed');

Common Assertion Patterns

Collection Size

Assert.areEqual(200, results.size(), 'Should process all 200 records');
Assert.isTrue(results.isEmpty(), 'No results expected for invalid input');
Assert.isFalse(results.isEmpty(), 'Results should not be empty');

Field Values

Assert.areEqual('Processed', acc.Status__c, 'Account status should be updated to Processed');

for (Account acc : updatedAccounts) {
    Assert.areEqual('Active', acc.Status__c,
        'Account ' + acc.Name + ' should have Active status');
}

Exception Testing

@isTest
static void shouldThrowException_WhenInputInvalid() {
    Test.startTest();
    try {
        MyService.process(null);
        Assert.fail('Expected MyCustomException for null input');
    } catch (MyCustomException e) {
        Assert.isTrue(e.getMessage().contains('cannot be null'),
            'Exception message should mention null input');
    }
    Test.stopTest();
}

DML Results

// Insert success
Database.SaveResult[] results = Database.insert(accounts, false);
for (Database.SaveResult sr : results) {
    Assert.isTrue(sr.isSuccess(), 'Insert should succeed: ' + sr.getErrors());
}

Database.SaveResult sr = Database.insert(invalidAccount, false);
Assert.isFalse(sr.isSuccess(), 'Insert should fail for invalid data');
Assert.isTrue(sr.getErrors()[0].getMessage().contains('REQUIRED_FIELD_MISSING'),
    'Error should indicate missing required field');

Null Checks

Assert.isNull(result.ErrorMessage__c, 'No error expected for valid input');
Assert.isNotNull(result.Id, 'Record should have been inserted');

Date/DateTime

Assert.areEqual(Date.today(), record.CreatedDate__c, 'Should be created today');
Assert.isTrue(record.DueDate__c >= Date.today(), 'Due date should be in the future');

Anti-Patterns

Anti-Pattern Fix
Assert.isTrue(results.size() > 0) Use Assert.areEqual(expectedCount, results.size(), ...)
Assert.isTrue(results.size() >= expected) Compute exact expected count, use Assert.areEqual
Testing implementation not behavior Assert on observable outcomes (field values, record counts)
Missing negative test assertions Verify the actual outcome, not just that no exception occurred
Assert.isTrue(count != 0) Use Assert.areEqual with deterministic value from test data