Standardized test method annotations to @IsTest and updated assertion methods to use System.Assert class

This commit is contained in:
Mitch Spano 2026-01-30 14:01:41 -06:00
parent 573249152f
commit 0db60a540a
6 changed files with 156 additions and 112 deletions

View File

@ -54,7 +54,7 @@ private class [ObjectName]TriggerHandlerTest {
// TODO: Add assertions // TODO: Add assertions
// List<Opportunity> inserted = [SELECT Id, Name FROM Opportunity WHERE Name = 'Valid Opp']; // List<Opportunity> inserted = [SELECT Id, Name FROM Opportunity WHERE Name = 'Valid Opp'];
// System.assertEquals(1, inserted.size(), 'Should insert 1 record'); // System.Assert.areEqual(1, inserted.size(), 'Should insert 1 record');
} }
/** /**
@ -82,13 +82,13 @@ private class [ObjectName]TriggerHandlerTest {
} catch (DmlException e) { } catch (DmlException e) {
exceptionThrown = true; exceptionThrown = true;
// TODO: Assert error message // TODO: Assert error message
// System.assert(e.getMessage().contains('must have Amount'), // System.Assert.isTrue(e.getMessage().contains('must have Amount'),
// 'Should throw validation error'); // 'Should throw validation error');
} }
Test.stopTest(); Test.stopTest();
// System.assert(exceptionThrown, 'Should have thrown an exception'); // System.Assert.isTrue(exceptionThrown, 'Should have thrown an exception');
} }
/** /**
@ -109,7 +109,7 @@ private class [ObjectName]TriggerHandlerTest {
// TODO: Add assertions // TODO: Add assertions
// Opportunity updated = [SELECT Description FROM Opportunity WHERE Id = :testOpps[0].Id]; // Opportunity updated = [SELECT Description FROM Opportunity WHERE Id = :testOpps[0].Id];
// System.assert(updated.Description.contains('Stage changed'), // System.Assert.isTrue(updated.Description.contains('Stage changed'),
// 'Description should be updated'); // 'Description should be updated');
} }
@ -141,7 +141,7 @@ private class [ObjectName]TriggerHandlerTest {
// TODO: Query for related records created by handler // TODO: Query for related records created by handler
// List<Task> createdTasks = [SELECT Id FROM Task]; // List<Task> createdTasks = [SELECT Id FROM Task];
// System.assertEquals(1, createdTasks.size(), 'Should create 1 task'); // System.Assert.areEqual(1, createdTasks.size(), 'Should create 1 task');
} }
/** /**
@ -162,8 +162,8 @@ private class [ObjectName]TriggerHandlerTest {
// TODO: Query for side effects (e.g., Tasks created) // TODO: Query for side effects (e.g., Tasks created)
// List<Task> tasks = [SELECT Id, Subject FROM Task WHERE WhatId = :testOpps[0].Id]; // List<Task> tasks = [SELECT Id, Subject FROM Task WHERE WhatId = :testOpps[0].Id];
// System.assertEquals(1, tasks.size(), 'Should create 1 task'); // System.Assert.areEqual(1, tasks.size(), 'Should create 1 task');
// System.assertEquals('Send thank-you', tasks[0].Subject); // System.Assert.areEqual('Send thank-you', tasks[0].Subject);
} }
/** /**
@ -191,7 +191,7 @@ private class [ObjectName]TriggerHandlerTest {
// TODO: Assert all records inserted successfully // TODO: Assert all records inserted successfully
// List<Opportunity> inserted = [SELECT Id FROM Opportunity WHERE Name LIKE 'Bulk Opp%']; // List<Opportunity> inserted = [SELECT Id FROM Opportunity WHERE Name LIKE 'Bulk Opp%'];
// System.assertEquals(200, inserted.size(), 'Should insert all 200 records'); // System.Assert.areEqual(200, inserted.size(), 'Should insert all 200 records');
} }
/** /**
@ -231,7 +231,7 @@ private class [ObjectName]TriggerHandlerTest {
// TODO: Assert only qualifying records triggered side effects // TODO: Assert only qualifying records triggered side effects
// List<Task> tasks = [SELECT Id FROM Task WHERE WhatId IN :testOpps]; // List<Task> tasks = [SELECT Id FROM Task WHERE WhatId IN :testOpps];
// System.assertEquals(25, tasks.size(), 'Should create tasks for 25 qualifying records'); // System.Assert.areEqual(25, tasks.size(), 'Should create tasks for 25 qualifying records');
} }
/** /**
@ -257,9 +257,9 @@ private class [ObjectName]TriggerHandlerTest {
Test.stopTest(); Test.stopTest();
// Assert we're under governor limits // Assert we're under governor limits
System.assert(Limits.getDmlStatements() < Limits.getLimitDmlStatements(), System.Assert.isTrue(Limits.getDmlStatements() < Limits.getLimitDmlStatements(),
'Should not exceed DML statement limit'); 'Should not exceed DML statement limit');
System.assert(Limits.getQueries() < Limits.getLimitQueries(), System.Assert.isTrue(Limits.getQueries() < Limits.getLimitQueries(),
'Should not exceed SOQL query limit'); 'Should not exceed SOQL query limit');
} }
@ -278,7 +278,7 @@ private class [ObjectName]TriggerHandlerTest {
Test.stopTest(); Test.stopTest();
// If we get here without exception, test passes // If we get here without exception, test passes
System.assert(true, 'Handler should handle empty collections gracefully'); System.Assert.isTrue(true, 'Handler should handle empty collections gracefully');
} }
/** /**
@ -310,11 +310,11 @@ private class [ObjectName]TriggerHandlerTest {
WHERE WhatId IN :oppIds WHERE WhatId IN :oppIds
]; ];
System.assertEquals(expectedCount, tasks.size(), System.Assert.areEqual(expectedCount, tasks.size(),
'Should create ' + expectedCount + ' task(s)'); 'Should create ' + expectedCount + ' task(s)');
if (expectedCount > 0) { if (expectedCount > 0) {
System.assertEquals(subject, tasks[0].Subject, System.Assert.areEqual(subject, tasks[0].Subject,
'Task subject should match'); 'Task subject should match');
} }
} }

View File

@ -16,7 +16,7 @@ description: Apex test class generation with TestDataFactory patterns, bulk test
## Test Class Structure ## Test Class Structure
```apex ```apex
@isTest @IsTest
private class MyServiceTest { private class MyServiceTest {
@TestSetup @TestSetup
@ -25,7 +25,7 @@ private class MyServiceTest {
List<Account> accounts = TestDataFactory.createAccounts(200, true); List<Account> accounts = TestDataFactory.createAccounts(200, true);
} }
@isTest @IsTest
static void shouldPerformExpectedBehavior_WhenValidInput() { static void shouldPerformExpectedBehavior_WhenValidInput() {
// Given: Setup specific test state // Given: Setup specific test state
List<Account> accounts = [SELECT Id, Name FROM Account]; List<Account> accounts = [SELECT Id, Name FROM Account];
@ -37,13 +37,13 @@ private class MyServiceTest {
// Then: Assert expected outcomes // Then: Assert expected outcomes
List<Account> updated = [SELECT Id, Status__c FROM Account]; List<Account> updated = [SELECT Id, Status__c FROM Account];
System.assertEquals(200, updated.size(), 'All accounts should be processed'); System.Assert.areEqual(200, updated.size(), 'All accounts should be processed');
for (Account acc : updated) { for (Account acc : updated) {
System.assertEquals('Processed', acc.Status__c, 'Status should be updated'); System.Assert.areEqual('Processed', acc.Status__c, 'Status should be updated');
} }
} }
@isTest @IsTest
static void shouldThrowException_WhenInvalidInput() { static void shouldThrowException_WhenInvalidInput() {
// Given // Given
List<Account> emptyList = new List<Account>(); List<Account> emptyList = new List<Account>();
@ -52,9 +52,9 @@ private class MyServiceTest {
Test.startTest(); Test.startTest();
try { try {
MyService.processAccounts(emptyList); MyService.processAccounts(emptyList);
System.assert(false, 'Expected MyCustomException to be thrown'); System.Assert.fail('Expected MyCustomException to be thrown');
} catch (MyCustomException e) { } catch (MyCustomException e) {
System.assert(e.getMessage().contains('cannot be empty'), System.Assert.isTrue(e.getMessage().contains('cannot be empty'),
'Exception message should indicate empty input'); 'Exception message should indicate empty input');
} }
Test.stopTest(); Test.stopTest();

View File

@ -2,28 +2,41 @@
## Assertion Methods ## Assertion Methods
The `System.Assert` class provides methods to assert various conditions in test methods. All methods support an optional message parameter for better error reporting.
| Method | Use Case | | Method | Use Case |
|--------|----------| |--------|----------|
| `System.assertEquals(expected, actual, msg)` | Exact equality | | `System.Assert.areEqual(expected, actual, msg)` | Exact equality |
| `System.assertNotEquals(expected, actual, msg)` | Value should differ | | `System.Assert.areNotEqual(notExpected, actual, msg)` | Value should differ |
| `System.assert(condition, msg)` | Boolean condition | | `System.Assert.isTrue(condition, msg)` | Boolean condition is true |
| `System.Assert.isFalse(condition, msg)` | Boolean condition is false |
| `System.Assert.isNull(value, msg)` | Value is null |
| `System.Assert.isNotNull(value, msg)` | Value is not null |
| `System.Assert.isInstanceOfType(instance, expectedType, msg)` | Instance is of specified type |
| `System.Assert.isNotInstanceOfType(instance, notExpectedType, msg)` | Instance is not of specified type |
| `System.Assert.fail(msg)` | Explicitly fail the test |
**Always include the third parameter (message)** - Makes test failures meaningful. **Always include the message parameter** - Makes test failures meaningful and easier to debug.
**Note:** Assertion failures are fatal errors that halt code execution. You cannot catch assertion failures using try/catch blocks, even though they're logged as exceptions.
**Note:** Call `startTest()` and `stopTest()` only once per test method. Wrap only the code under test between these calls, not setup or verification code.
## Good vs Bad Assertions ## Good vs Bad Assertions
### ❌ Bad: No message, tests coverage not behavior ### ❌ Bad: No message, tests coverage not behavior
```apex ```apex
System.assertEquals(true, result); System.Assert.areEqual(true, result);
System.assert(accounts.size() > 0); System.Assert.isTrue(accounts.size() > 0);
``` ```
### ✅ Good: Descriptive message, tests specific behavior ### ✅ Good: Descriptive message, tests specific behavior
```apex ```apex
System.assertEquals(true, result, 'Service should return true for valid input'); System.Assert.areEqual(true, result, 'Service should return true for valid input');
System.assertEquals(200, accounts.size(), 'All 200 accounts should be processed'); System.Assert.areEqual(200, accounts.size(), 'All 200 accounts should be processed');
``` ```
## Common Assertion Patterns ## Common Assertion Patterns
@ -32,24 +45,24 @@ System.assertEquals(200, accounts.size(), 'All 200 accounts should be processed'
```apex ```apex
// Exact count // Exact count
System.assertEquals(200, results.size(), 'Should process all 200 records'); System.Assert.areEqual(200, results.size(), 'Should process all 200 records');
// Not empty // Not empty
System.assert(!results.isEmpty(), 'Results should not be empty'); System.Assert.isFalse(results.isEmpty(), 'Results should not be empty');
// Empty // Empty
System.assert(results.isEmpty(), 'No results expected for invalid input'); System.Assert.isTrue(results.isEmpty(), 'No results expected for invalid input');
``` ```
### Field Values ### Field Values
```apex ```apex
// Single record // Single record
System.assertEquals('Processed', acc.Status__c, 'Account status should be updated to Processed'); System.Assert.areEqual('Processed', acc.Status__c, 'Account status should be updated to Processed');
// All records in collection // All records in collection
for (Account acc : updatedAccounts) { for (Account acc : updatedAccounts) {
System.assertEquals('Active', acc.Status__c, System.Assert.areEqual('Active', acc.Status__c,
'Account ' + acc.Name + ' should have Active status'); 'Account ' + acc.Name + ' should have Active status');
} }
``` ```
@ -57,8 +70,8 @@ for (Account acc : updatedAccounts) {
### Exception Testing ### Exception Testing
```apex ```apex
@isTest @IsTest
static void shouldThrowException_WhenInputInvalid() { private static void shouldThrowException_WhenInputInvalid() {
Boolean exceptionThrown = false; Boolean exceptionThrown = false;
String exceptionMessage = ''; String exceptionMessage = '';
@ -71,8 +84,8 @@ static void shouldThrowException_WhenInputInvalid() {
} }
Test.stopTest(); Test.stopTest();
System.assert(exceptionThrown, 'MyCustomException should be thrown for null input'); System.Assert.isTrue(exceptionThrown, 'MyCustomException should be thrown for null input');
System.assert(exceptionMessage.contains('cannot be null'), System.Assert.isTrue(exceptionMessage.contains('cannot be null'),
'Exception message should mention null input'); 'Exception message should mention null input');
} }
``` ```
@ -83,13 +96,13 @@ static void shouldThrowException_WhenInputInvalid() {
// Insert success // Insert success
Database.SaveResult[] results = Database.insert(accounts, false); Database.SaveResult[] results = Database.insert(accounts, false);
for (Database.SaveResult sr : results) { for (Database.SaveResult sr : results) {
System.assert(sr.isSuccess(), 'Insert should succeed: ' + sr.getErrors()); System.Assert.isTrue(sr.isSuccess(), 'Insert should succeed: ' + sr.getErrors());
} }
// Expected failures // Expected failures
Database.SaveResult sr = Database.insert(invalidAccount, false); Database.SaveResult sr = Database.insert(invalidAccount, false);
System.assert(!sr.isSuccess(), 'Insert should fail for invalid data'); System.Assert.isFalse(sr.isSuccess(), 'Insert should fail for invalid data');
System.assert(sr.getErrors()[0].getMessage().contains('REQUIRED_FIELD_MISSING'), System.Assert.isTrue(sr.getErrors()[0].getMessage().contains('REQUIRED_FIELD_MISSING'),
'Error should indicate missing required field'); 'Error should indicate missing required field');
``` ```
@ -97,11 +110,11 @@ System.assert(sr.getErrors()[0].getMessage().contains('REQUIRED_FIELD_MISSING'),
```apex ```apex
// Compare specific fields, not entire objects // Compare specific fields, not entire objects
System.assertEquals(expected.Name, actual.Name, 'Names should match'); System.Assert.areEqual(expected.Name, actual.Name, 'Names should match');
System.assertEquals(expected.Status__c, actual.Status__c, 'Status should match'); System.Assert.areEqual(expected.Status__c, actual.Status__c, 'Status should match');
// Or use JSON for deep comparison (use sparingly) // Or use JSON for deep comparison (use sparingly)
System.assertEquals( System.Assert.areEqual(
JSON.serialize(expected), JSON.serialize(expected),
JSON.serialize(actual), JSON.serialize(actual),
'Objects should be identical' 'Objects should be identical'
@ -112,11 +125,11 @@ System.assertEquals(
```apex ```apex
// Exact date // Exact date
System.assertEquals(Date.today(), record.CreatedDate__c, 'Should be created today'); System.Assert.areEqual(Date.today(), record.CreatedDate__c, 'Should be created today');
// Date within range // Date within range
System.assert(record.DueDate__c >= Date.today(), 'Due date should be in the future'); System.Assert.isTrue(record.DueDate__c >= Date.today(), 'Due date should be in the future');
System.assert(record.DueDate__c <= Date.today().addDays(30), System.Assert.isTrue(record.DueDate__c <= Date.today().addDays(30),
'Due date should be within 30 days'); 'Due date should be within 30 days');
``` ```
@ -124,10 +137,41 @@ System.assert(record.DueDate__c <= Date.today().addDays(30),
```apex ```apex
// Should be null // Should be null
System.assertEquals(null, result.ErrorMessage__c, 'No error expected for valid input'); System.Assert.isNull(result.ErrorMessage__c, 'No error expected for valid input');
// Should not be null // Should not be null
System.assertNotEquals(null, result.Id, 'Record should have been inserted'); System.Assert.isNotNull(result.Id, 'Record should have been inserted');
```
### Type Checking
```apex
// Verify instance is of expected type
Object result = MyService.processData();
System.Assert.isInstanceOfType(result, MyCustomClass.class,
'Result should be an instance of MyCustomClass');
// Verify instance is not of a specific type
Object handler = HandlerFactory.create('Account');
System.Assert.isNotInstanceOfType(handler, ContactHandler.class,
'Account handler should not be a ContactHandler');
```
### Explicit Test Failures
```apex
// Use Assert.fail() when an exception should have been thrown but wasn't
@IsTest
private static void shouldThrowException_WhenInputInvalid() {
try {
MyService.process(null);
System.Assert.fail('Expected MyCustomException to be thrown for null input');
} catch (MyCustomException e) {
// Exception was thrown as expected, test passes
System.Assert.isTrue(e.getMessage().contains('cannot be null'),
'Exception message should mention null input');
}
}
``` ```
## Anti-Patterns to Avoid ## Anti-Patterns to Avoid
@ -136,20 +180,20 @@ System.assertNotEquals(null, result.Id, 'Record should have been inserted');
```apex ```apex
// Bad: Testing that a specific method was called // Bad: Testing that a specific method was called
System.assert(MyClass.methodWasCalled, 'Method should be called'); System.Assert.isTrue(MyClass.methodWasCalled, 'Method should be called');
// Good: Testing the observable outcome // Good: Testing the observable outcome
System.assertEquals('Expected Value', record.Field__c, 'Field should be updated'); System.Assert.areEqual('Expected Value', record.Field__c, 'Field should be updated');
``` ```
### ❌ Overly generic assertions ### ❌ Overly generic assertions
```apex ```apex
// Bad: Passes for any non-empty result // Bad: Passes for any non-empty result
System.assert(results.size() > 0); System.Assert.isTrue(results.size() > 0);
// Good: Verifies exact expected count // Good: Verifies exact expected count
System.assertEquals(200, results.size(), 'All 200 records should be returned'); System.Assert.areEqual(200, results.size(), 'All 200 records should be returned');
``` ```
### ❌ Missing negative test assertions ### ❌ Missing negative test assertions
@ -160,6 +204,6 @@ MyService.process(data); // Test passes if no exception
// Good: Verifies the actual outcome // Good: Verifies the actual outcome
Result r = MyService.process(data); Result r = MyService.process(data);
System.assertEquals('Success', r.status, 'Processing should succeed'); System.Assert.areEqual('Success', r.status, 'Processing should succeed');
System.assertEquals(0, r.errorCount, 'No errors should occur'); System.Assert.areEqual(0, r.errorCount, 'No errors should occur');
``` ```

View File

@ -9,8 +9,8 @@
### Basic Batch Test ### Basic Batch Test
```apex ```apex
@isTest @IsTest
static void shouldProcessAllRecords_WhenBatchExecutes() { private static void shouldProcessAllRecords_WhenBatchExecutes() {
// Given: Create test data // Given: Create test data
List<Account> accounts = TestDataFactory.createAccounts(200, true); List<Account> accounts = TestDataFactory.createAccounts(200, true);
@ -23,7 +23,7 @@ static void shouldProcessAllRecords_WhenBatchExecutes() {
// Then: Verify results // Then: Verify results
List<Account> updated = [SELECT Id, Status__c FROM Account]; List<Account> updated = [SELECT Id, Status__c FROM Account];
for (Account acc : updated) { for (Account acc : updated) {
System.assertEquals('Processed', acc.Status__c, System.Assert.areEqual('Processed', acc.Status__c,
'Batch should update all account statuses'); 'Batch should update all account statuses');
} }
} }
@ -32,8 +32,8 @@ static void shouldProcessAllRecords_WhenBatchExecutes() {
### Testing Batch with Failures ### Testing Batch with Failures
```apex ```apex
@isTest @IsTest
static void shouldLogErrors_WhenRecordsFail() { private static void shouldLogErrors_WhenRecordsFail() {
// Given: Create mix of valid and invalid records // Given: Create mix of valid and invalid records
List<Account> accounts = TestDataFactory.createAccounts(198, true); List<Account> accounts = TestDataFactory.createAccounts(198, true);
@ -55,15 +55,15 @@ static void shouldLogErrors_WhenRecordsFail() {
// Then // Then
List<Error_Log__c> errors = [SELECT Id, Message__c FROM Error_Log__c]; List<Error_Log__c> errors = [SELECT Id, Message__c FROM Error_Log__c];
System.assertEquals(2, errors.size(), 'Should log 2 failed records'); System.Assert.areEqual(2, errors.size(), 'Should log 2 failed records');
} }
``` ```
### Testing Batch Scope ### Testing Batch Scope
```apex ```apex
@isTest @IsTest
static void shouldRespectBatchSize() { private static void shouldRespectBatchSize() {
// Given // Given
List<Account> accounts = TestDataFactory.createAccounts(250, true); List<Account> accounts = TestDataFactory.createAccounts(250, true);
@ -74,7 +74,7 @@ static void shouldRespectBatchSize() {
// Note: In tests, all batches execute but you can verify total processing // Note: In tests, all batches execute but you can verify total processing
List<Account> processed = [SELECT Id FROM Account WHERE Processed__c = true]; List<Account> processed = [SELECT Id FROM Account WHERE Processed__c = true];
System.assertEquals(250, processed.size(), 'All records should be processed'); System.Assert.areEqual(250, processed.size(), 'All records should be processed');
} }
``` ```
@ -83,8 +83,8 @@ static void shouldRespectBatchSize() {
### Basic Queueable Test ### Basic Queueable Test
```apex ```apex
@isTest @IsTest
static void shouldCompleteProcessing_WhenQueueableEnqueued() { private static void shouldCompleteProcessing_WhenQueueableEnqueued() {
// Given // Given
Account acc = TestDataFactory.createAccount(true); Account acc = TestDataFactory.createAccount(true);
@ -96,7 +96,7 @@ static void shouldCompleteProcessing_WhenQueueableEnqueued() {
// Then // Then
Account updated = [SELECT Id, Status__c FROM Account WHERE Id = :acc.Id]; Account updated = [SELECT Id, Status__c FROM Account WHERE Id = :acc.Id];
System.assertEquals('Processed', updated.Status__c, System.Assert.areEqual('Processed', updated.Status__c,
'Queueable should update account status'); 'Queueable should update account status');
} }
``` ```
@ -106,8 +106,8 @@ static void shouldCompleteProcessing_WhenQueueableEnqueued() {
Chained queueables only execute the first job in tests: Chained queueables only execute the first job in tests:
```apex ```apex
@isTest @IsTest
static void shouldChainNextJob_WhenMoreRecordsExist() { private static void shouldChainNextJob_WhenMoreRecordsExist() {
// Given: More records than one queueable can process // Given: More records than one queueable can process
List<Account> accounts = TestDataFactory.createAccounts(500, true); List<Account> accounts = TestDataFactory.createAccounts(500, true);
@ -119,7 +119,7 @@ static void shouldChainNextJob_WhenMoreRecordsExist() {
// Verify first batch processed // Verify first batch processed
List<Account> processed = [SELECT Id FROM Account WHERE Processed__c = true]; List<Account> processed = [SELECT Id FROM Account WHERE Processed__c = true];
System.assertEquals(100, processed.size(), 'First batch should process 100 records'); System.Assert.areEqual(100, processed.size(), 'First batch should process 100 records');
// Verify chain was enqueued (check AsyncApexJob) // Verify chain was enqueued (check AsyncApexJob)
List<AsyncApexJob> jobs = [ List<AsyncApexJob> jobs = [
@ -127,15 +127,15 @@ static void shouldChainNextJob_WhenMoreRecordsExist() {
FROM AsyncApexJob FROM AsyncApexJob
WHERE ApexClass.Name = 'MyChainedQueueable' WHERE ApexClass.Name = 'MyChainedQueueable'
]; ];
System.assert(jobs.size() >= 1, 'Chained job should be enqueued'); System.Assert.isTrue(jobs.size() >= 1, 'Chained job should be enqueued');
} }
``` ```
### Testing Queueable with Callouts ### Testing Queueable with Callouts
```apex ```apex
@isTest @IsTest
static void shouldMakeCallout_WhenQueueableWithCallout() { private static void shouldMakeCallout_WhenQueueableWithCallout() {
// Given // Given
Test.setMock(HttpCalloutMock.class, new MockHttpResponse(200, '{"status":"ok"}')); Test.setMock(HttpCalloutMock.class, new MockHttpResponse(200, '{"status":"ok"}'));
Account acc = TestDataFactory.createAccount(true); Account acc = TestDataFactory.createAccount(true);
@ -148,7 +148,7 @@ static void shouldMakeCallout_WhenQueueableWithCallout() {
// Then // Then
Account updated = [SELECT Id, External_Status__c FROM Account WHERE Id = :acc.Id]; Account updated = [SELECT Id, External_Status__c FROM Account WHERE Id = :acc.Id];
System.assertEquals('Synced', updated.External_Status__c, System.Assert.areEqual('Synced', updated.External_Status__c,
'Should update status after successful callout'); 'Should update status after successful callout');
} }
``` ```
@ -156,8 +156,8 @@ static void shouldMakeCallout_WhenQueueableWithCallout() {
## Future Method Testing ## Future Method Testing
```apex ```apex
@isTest @IsTest
static void shouldExecuteFutureMethod() { private static void shouldExecuteFutureMethod() {
// Given // Given
Account acc = TestDataFactory.createAccount(true); Account acc = TestDataFactory.createAccount(true);
@ -168,7 +168,7 @@ static void shouldExecuteFutureMethod() {
// Then // Then
Account updated = [SELECT Id, Processed__c FROM Account WHERE Id = :acc.Id]; Account updated = [SELECT Id, Processed__c FROM Account WHERE Id = :acc.Id];
System.assertEquals(true, updated.Processed__c, 'Future should process record'); System.Assert.areEqual(true, updated.Processed__c, 'Future should process record');
} }
``` ```
@ -177,8 +177,8 @@ static void shouldExecuteFutureMethod() {
### Testing Scheduled Execution ### Testing Scheduled Execution
```apex ```apex
@isTest @IsTest
static void shouldExecuteScheduledJob() { private static void shouldExecuteScheduledJob() {
// Given // Given
List<Account> accounts = TestDataFactory.createAccounts(50, true); List<Account> accounts = TestDataFactory.createAccounts(50, true);
@ -194,15 +194,15 @@ static void shouldExecuteScheduledJob() {
// Then // Then
List<Account> processed = [SELECT Id FROM Account WHERE Processed__c = true]; List<Account> processed = [SELECT Id FROM Account WHERE Processed__c = true];
System.assertEquals(50, processed.size(), 'Scheduled job should process records'); System.Assert.areEqual(50, processed.size(), 'Scheduled job should process records');
} }
``` ```
### Testing Schedule Registration ### Testing Schedule Registration
```apex ```apex
@isTest @IsTest
static void shouldScheduleJob() { private static void shouldScheduleJob() {
Test.startTest(); Test.startTest();
String cronExp = '0 0 6 * * ?'; // Daily at 6 AM String cronExp = '0 0 6 * * ?'; // Daily at 6 AM
String jobId = System.schedule('Daily Processing', cronExp, new MyScheduledClass()); String jobId = System.schedule('Daily Processing', cronExp, new MyScheduledClass());
@ -214,16 +214,16 @@ static void shouldScheduleJob() {
FROM CronTrigger FROM CronTrigger
WHERE Id = :jobId WHERE Id = :jobId
]; ];
System.assertEquals('0 0 6 * * ?', ct.CronExpression, 'CRON should match'); System.Assert.areEqual('0 0 6 * * ?', ct.CronExpression, 'CRON should match');
System.assertEquals('WAITING', ct.State, 'Job should be waiting'); System.Assert.areEqual('WAITING', ct.State, 'Job should be waiting');
} }
``` ```
## Testing Async Limits ## Testing Async Limits
```apex ```apex
@isTest @IsTest
static void shouldNotExceedQueueableLimits() { private static void shouldNotExceedQueueableLimits() {
// Given: Setup that might enqueue multiple jobs // Given: Setup that might enqueue multiple jobs
List<Account> accounts = TestDataFactory.createAccounts(100, true); List<Account> accounts = TestDataFactory.createAccounts(100, true);
@ -236,7 +236,7 @@ static void shouldNotExceedQueueableLimits() {
Test.stopTest(); Test.stopTest();
// Verify limit not exceeded (50 in synchronous context, 1 in queueable) // Verify limit not exceeded (50 in synchronous context, 1 in queueable)
System.assert(queueablesUsed <= 50, System.Assert.isTrue(queueablesUsed <= 50,
'Should not exceed queueable limit. Used: ' + queueablesUsed); 'Should not exceed queueable limit. Used: ' + queueablesUsed);
} }
``` ```
@ -252,7 +252,7 @@ System.enqueueJob(new MyQueueable());
// Missing Test.stopTest()! // Missing Test.stopTest()!
List<Account> results = [SELECT Id FROM Account WHERE Processed__c = true]; List<Account> results = [SELECT Id FROM Account WHERE Processed__c = true];
System.assertEquals(100, results.size()); // FAILS - queueable didn't run System.Assert.areEqual(100, results.size()); // FAILS - queueable didn't run
``` ```
### ❌ Testing chained jobs without understanding limits ### ❌ Testing chained jobs without understanding limits

View File

@ -7,7 +7,7 @@ Apex doesn't allow real HTTP callouts in tests. Use `HttpCalloutMock` interface.
### Basic Mock Implementation ### Basic Mock Implementation
```apex ```apex
@isTest @IsTest
public class MockHttpResponse implements HttpCalloutMock { public class MockHttpResponse implements HttpCalloutMock {
private Integer statusCode; private Integer statusCode;
@ -31,8 +31,8 @@ public class MockHttpResponse implements HttpCalloutMock {
### Using the Mock ### Using the Mock
```apex ```apex
@isTest @IsTest
static void shouldProcessApiResponse_WhenCalloutSucceeds() { private static void shouldProcessApiResponse_WhenCalloutSucceeds() {
// Given // Given
String mockResponse = '{"status": "success", "data": [{"id": "123"}]}'; String mockResponse = '{"status": "success", "data": [{"id": "123"}]}';
Test.setMock(HttpCalloutMock.class, new MockHttpResponse(200, mockResponse)); Test.setMock(HttpCalloutMock.class, new MockHttpResponse(200, mockResponse));
@ -43,12 +43,12 @@ static void shouldProcessApiResponse_WhenCalloutSucceeds() {
Test.stopTest(); Test.stopTest();
// Then // Then
System.assertEquals(1, results.size(), 'Should parse one record from response'); System.Assert.areEqual(1, results.size(), 'Should parse one record from response');
System.assertEquals('123', results[0].externalId, 'Should extract correct ID'); System.Assert.areEqual('123', results[0].externalId, 'Should extract correct ID');
} }
@isTest @IsTest
static void shouldHandleError_WhenCalloutFails() { private static void shouldHandleError_WhenCalloutFails() {
// Given // Given
String errorResponse = '{"error": "Unauthorized"}'; String errorResponse = '{"error": "Unauthorized"}';
Test.setMock(HttpCalloutMock.class, new MockHttpResponse(401, errorResponse)); Test.setMock(HttpCalloutMock.class, new MockHttpResponse(401, errorResponse));
@ -59,8 +59,8 @@ static void shouldHandleError_WhenCalloutFails() {
Test.stopTest(); Test.stopTest();
// Then // Then
System.assertEquals(false, result.isSuccess, 'Should indicate failure'); System.Assert.areEqual(false, result.isSuccess, 'Should indicate failure');
System.assert(result.errorMessage.contains('Unauthorized'), 'Should capture error'); System.Assert.isTrue(result.errorMessage.contains('Unauthorized'), 'Should capture error');
} }
``` ```
@ -69,7 +69,7 @@ static void shouldHandleError_WhenCalloutFails() {
For services making multiple callouts: For services making multiple callouts:
```apex ```apex
@isTest @IsTest
public class MultiRequestMock implements HttpCalloutMock { public class MultiRequestMock implements HttpCalloutMock {
private Map<String, HttpResponse> endpointResponses; private Map<String, HttpResponse> endpointResponses;
@ -116,8 +116,8 @@ Test.setMock(HttpCalloutMock.class, new MultiRequestMock(mocks));
For complex response bodies, store JSON in Static Resources: For complex response bodies, store JSON in Static Resources:
```apex ```apex
@isTest @IsTest
static void shouldParseComplexResponse() { private static void shouldParseComplexResponse() {
StaticResourceCalloutMock mock = new StaticResourceCalloutMock(); StaticResourceCalloutMock mock = new StaticResourceCalloutMock();
mock.setStaticResource('TestApiResponse'); // Static Resource name mock.setStaticResource('TestApiResponse'); // Static Resource name
mock.setStatusCode(200); mock.setStatusCode(200);
@ -129,7 +129,7 @@ static void shouldParseComplexResponse() {
Result r = MyService.callExternalApi(); Result r = MyService.callExternalApi();
Test.stopTest(); Test.stopTest();
System.assertNotEquals(null, r, 'Should parse response'); System.Assert.isNotNull(r, 'Should parse response');
} }
``` ```
@ -138,7 +138,7 @@ static void shouldParseComplexResponse() {
For mocking Apex class dependencies using `System.StubProvider`: For mocking Apex class dependencies using `System.StubProvider`:
```apex ```apex
@isTest @IsTest
public class MyServiceMock implements System.StubProvider { public class MyServiceMock implements System.StubProvider {
public Object handleMethodCall( public Object handleMethodCall(
@ -157,8 +157,8 @@ public class MyServiceMock implements System.StubProvider {
} }
// Usage in test: // Usage in test:
@isTest @IsTest
static void shouldUseAccountData() { private static void shouldUseAccountData() {
MyServiceMock mockProvider = new MyServiceMock(); MyServiceMock mockProvider = new MyServiceMock();
IMyService mockService = (IMyService)Test.createStub(IMyService.class, mockProvider); IMyService mockService = (IMyService)Test.createStub(IMyService.class, mockProvider);
@ -169,7 +169,7 @@ static void shouldUseAccountData() {
String result = controller.displayAccountInfo(); String result = controller.displayAccountInfo();
Test.stopTest(); Test.stopTest();
System.assert(result.contains('Mock Account'), 'Should use mocked data'); System.Assert.isTrue(result.contains('Mock Account'), 'Should use mocked data');
} }
``` ```
@ -178,8 +178,8 @@ static void shouldUseAccountData() {
Apex sends real emails by default. Use limits to verify: Apex sends real emails by default. Use limits to verify:
```apex ```apex
@isTest @IsTest
static void shouldSendEmail_WhenTriggered() { private static void shouldSendEmail_WhenTriggered() {
Integer emailsBefore = Limits.getEmailInvocations(); Integer emailsBefore = Limits.getEmailInvocations();
Test.startTest(); Test.startTest();
@ -187,7 +187,7 @@ static void shouldSendEmail_WhenTriggered() {
Test.stopTest(); Test.stopTest();
// Verify email was queued (not actually sent in tests) // Verify email was queued (not actually sent in tests)
System.assertEquals( System.Assert.areEqual(
emailsBefore + 1, emailsBefore + 1,
Limits.getEmailInvocations(), Limits.getEmailInvocations(),
'One email should be sent' 'One email should be sent'
@ -198,8 +198,8 @@ static void shouldSendEmail_WhenTriggered() {
## Platform Event Testing ## Platform Event Testing
```apex ```apex
@isTest @IsTest
static void shouldPublishEvent_WhenRecordCreated() { private static void shouldPublishEvent_WhenRecordCreated() {
Test.startTest(); Test.startTest();
// Enable event delivery in test context // Enable event delivery in test context
@ -214,6 +214,6 @@ static void shouldPublishEvent_WhenRecordCreated() {
// Query platform event trigger results // Query platform event trigger results
List<EventLog__c> logs = [SELECT Id FROM EventLog__c WHERE AccountId__c = :acc.Id]; List<EventLog__c> logs = [SELECT Id FROM EventLog__c WHERE AccountId__c = :acc.Id];
System.assertEquals(1, logs.size(), 'Event handler should create log record'); System.Assert.areEqual(1, logs.size(), 'Event handler should create log record');
} }
``` ```

View File

@ -7,7 +7,7 @@ TestDataFactory is a centralized utility class for creating test records with se
## Base Template ## Base Template
```apex ```apex
@isTest @IsTest
public class TestDataFactory { public class TestDataFactory {
// ============ ACCOUNTS ============ // ============ ACCOUNTS ============