| name | sf-testing |
| description | Comprehensive Salesforce testing skill with test execution, code coverage analysis, and agentic test-fix loops. Run Apex tests, analyze coverage, generate test patterns, and automatically fix failing tests with 120-point scoring. |
| license | MIT |
| metadata | [object Object] |
sf-testing: Salesforce Test Execution & Coverage Analysis
Expert testing engineer specializing in Apex test execution, code coverage analysis, mock frameworks, and agentic test-fix loops. Execute tests, analyze failures, and automatically fix issues.
Core Responsibilities
- Test Execution: Run Apex tests via
sf apex run testwith coverage analysis - Coverage Analysis: Parse coverage reports, identify untested code paths
- Failure Analysis: Parse test failures, identify root causes, suggest fixes
- Agentic Test-Fix Loop: Automatically fix failing tests and re-run until passing
- Test Generation: Create test classes using sf-apex patterns
- Bulk Testing: Validate with 251+ records for governor limit safety
Workflow (5-Phase Pattern)
Phase 1: Test Discovery
Use AskUserQuestion to gather:
- Test scope (single class, all tests, specific test suite)
- Target org alias
- Coverage threshold requirement (default: 75%, recommended: 90%)
- Whether to enable agentic fix loop
Then:
- Check existing tests:
Glob: **/*Test*.cls,Glob: **/*_Test.cls - Check for Test Data Factories:
Glob: **/*TestDataFactory*.cls - Create TodoWrite tasks
Phase 2: Test Execution
Run Single Test Class:
sf apex run test --class-names MyClassTest --code-coverage --result-format json --output-dir test-results --target-org [alias]
Run All Tests:
sf apex run test --test-level RunLocalTests --code-coverage --result-format json --output-dir test-results --target-org [alias]
Run Specific Methods:
sf apex run test --tests MyClassTest.testMethod1 --tests MyClassTest.testMethod2 --code-coverage --result-format json --target-org [alias]
Run Test Suite:
sf apex run test --suite-names MySuite --code-coverage --result-format json --target-org [alias]
Phase 3: Results Analysis
Parse test-results JSON:
Read: test-results/test-run-id.json
Coverage Summary Output:
📊 TEST EXECUTION RESULTS
════════════════════════════════════════════════════════════════
Test Run ID: 707xx0000000000
Org: my-sandbox
Duration: 45.2s
SUMMARY
───────────────────────────────────────────────────────────────
✅ Passed: 42
❌ Failed: 3
⏭️ Skipped: 0
📈 Coverage: 78.5%
FAILED TESTS
───────────────────────────────────────────────────────────────
❌ AccountServiceTest.testBulkInsert
Line 45: System.AssertException: Assertion Failed
Expected: 200, Actual: 199
❌ LeadScoringTest.testNullHandling
Line 23: System.NullPointerException: Attempt to de-reference null
❌ OpportunityTriggerTest.testValidation
Line 67: System.DmlException: FIELD_CUSTOM_VALIDATION_EXCEPTION
COVERAGE BY CLASS
───────────────────────────────────────────────────────────────
Class Lines Covered Uncovered %
AccountService 150 142 8 94.7% ✅
LeadScoringService 85 68 17 80.0% ✅
OpportunityTrigger 45 28 17 62.2% ⚠️
ContactHelper 30 15 15 50.0% ❌
UNCOVERED LINES (OpportunityTrigger)
───────────────────────────────────────────────────────────────
Lines 23-28: Exception handling block
Lines 45-52: Bulk processing edge case
Lines 78-82: Null check branch
Phase 4: Agentic Test-Fix Loop
When tests fail, automatically:
┌─────────────────────────────────────────────────────────────────┐
│ AGENTIC TEST-FIX LOOP │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. Parse failure message and stack trace │
│ 2. Identify root cause: │
│ - Assertion failure → Check expected vs actual │
│ - NullPointerException → Add null checks │
│ - DmlException → Check validation rules, required fields │
│ - LimitException → Reduce SOQL/DML in test │
│ 3. Read the failing test class │
│ 4. Read the class under test │
│ 5. Generate fix using sf-apex skill │
│ 6. Re-run the specific failing test │
│ 7. Repeat until passing (max 3 attempts) │
│ │
└─────────────────────────────────────────────────────────────────┘
Failure Analysis Decision Tree:
| Error Type | Root Cause | Auto-Fix Strategy |
|---|---|---|
System.AssertException |
Wrong expected value or logic bug | Analyze assertion, check if test or code is wrong |
System.NullPointerException |
Missing null check or test data | Add null safety or fix test data setup |
System.DmlException |
Validation rule, required field, trigger | Check org config, add required fields to test data |
System.LimitException |
Governor limit hit | Refactor to use bulkified patterns |
System.QueryException |
No rows returned | Add test data or adjust query |
System.TypeException |
Type mismatch | Fix type casting or data format |
Auto-Fix Command:
Skill(skill="sf-apex", args="Fix failing test [TestClassName].[methodName] - Error: [error message]")
Phase 5: Coverage Improvement
If coverage < threshold:
- Identify Uncovered Lines:
sf apex run test --class-names MyClassTest --code-coverage --detailed-coverage --result-format json --target-org [alias]
- Generate Tests for Uncovered Code:
Read: force-app/main/default/classes/MyClass.cls (lines 45-52)
Then use sf-apex to generate test methods targeting those lines.
- Bulk Test Validation:
Skill(skill="sf-data", args="Create 251 [ObjectName] records for bulk testing")
- Re-run with New Tests:
sf apex run test --class-names MyClassTest --code-coverage --result-format json --target-org [alias]
Best Practices (120-Point Scoring)
| Category | Points | Key Rules |
|---|---|---|
| Test Coverage | 25 | 90%+ class coverage; all public methods tested; edge cases covered |
| Assertion Quality | 25 | Assert class used; meaningful messages; positive AND negative tests |
| Bulk Testing | 20 | Test with 251+ records; verify no SOQL/DML in loops under load |
| Test Data | 20 | Test Data Factory used; no hardcoded IDs; @TestSetup for efficiency |
| Isolation | 15 | SeeAllData=false; no org dependencies; mock external callouts |
| Documentation | 15 | Test method names describe scenario; comments for complex setup |
Scoring Thresholds:
⭐⭐⭐⭐⭐ 108-120 pts (90%+) → Production Ready
⭐⭐⭐⭐ 96-107 pts (80-89%) → Good, minor improvements
⭐⭐⭐ 84-95 pts (70-79%) → Acceptable, needs work
⭐⭐ 72-83 pts (60-69%) → Below standard
⭐ <72 pts (<60%) → BLOCKED - Major issues
⛔ TESTING GUARDRAILS (MANDATORY)
BEFORE running tests, verify:
| Check | Command | Why |
|---|---|---|
| Org authenticated | sf org display --target-org [alias] |
Tests need valid org connection |
| Classes deployed | sf project deploy report --target-org [alias] |
Can't test undeployed code |
| Test data exists | Check @TestSetup or TestDataFactory | Tests need data to operate on |
NEVER do these:
| Anti-Pattern | Problem | Correct Pattern |
|---|---|---|
@IsTest(SeeAllData=true) |
Tests depend on org data, break in clean orgs | Always SeeAllData=false (default) |
| Hardcoded Record IDs | IDs differ between orgs | Query or create in test |
| No assertions | Tests pass without validating anything | Assert every expected outcome |
| Single record tests only | Misses bulk trigger issues | Always test with 200+ records |
Test.startTest() without Test.stopTest() |
Async code won't execute | Always pair start/stop |
CLI Command Reference
Test Execution Commands
| Command | Purpose | Example |
|---|---|---|
sf apex run test |
Run tests | See examples above |
sf apex get test |
Get async test status | sf apex get test --test-run-id 707xx... |
sf apex list log |
List debug logs | sf apex list log --target-org alias |
sf apex tail log |
Stream logs real-time | sf apex tail log --target-org alias |
Useful Flags
| Flag | Purpose |
|---|---|
--code-coverage |
Include coverage in results |
--detailed-coverage |
Line-by-line coverage (slower) |
--result-format json |
Machine-parseable output |
--output-dir |
Save results to directory |
--synchronous |
Wait for completion (default) |
--test-level RunLocalTests |
All tests except managed packages |
--test-level RunAllTestsInOrg |
Every test including packages |
Test Patterns & Templates
Pattern 1: Basic Test Class
Use template: templates/basic-test.cls
@IsTest
private class AccountServiceTest {
@TestSetup
static void setupTestData() {
// Use Test Data Factory for consistent data creation
List<Account> accounts = TestDataFactory.createAccounts(5);
insert accounts;
}
@IsTest
static void testCreateAccount_Success() {
// Given
Account testAccount = new Account(Name = 'Test Account');
// When
Test.startTest();
Id accountId = AccountService.createAccount(testAccount);
Test.stopTest();
// Then
Assert.isNotNull(accountId, 'Account ID should not be null');
Account inserted = [SELECT Name FROM Account WHERE Id = :accountId];
Assert.areEqual('Test Account', inserted.Name, 'Account name should match');
}
@IsTest
static void testCreateAccount_NullInput_ThrowsException() {
// Given
Account nullAccount = null;
// When/Then
try {
Test.startTest();
AccountService.createAccount(nullAccount);
Test.stopTest();
Assert.fail('Expected IllegalArgumentException was not thrown');
} catch (IllegalArgumentException e) {
Assert.isTrue(e.getMessage().contains('cannot be null'),
'Error message should mention null: ' + e.getMessage());
}
}
}
Pattern 2: Bulk Test (251+ Records)
Use template: templates/bulk-test.cls
@IsTest
static void testBulkInsert_251Records() {
// Given - 251 records crosses the 200-record batch boundary
List<Account> accounts = TestDataFactory.createAccounts(251);
// When
Test.startTest();
insert accounts; // Triggers fire in batches of 200, then 51
Test.stopTest();
// Then
Integer count = [SELECT COUNT() FROM Account];
Assert.areEqual(251, count, 'All 251 accounts should be inserted');
// Verify no governor limits hit
Assert.isTrue(Limits.getQueries() < 100,
'Should not approach SOQL limit: ' + Limits.getQueries());
}
Pattern 3: Mock Callout Test
Use template: templates/mock-callout-test.cls
@IsTest
private class ExternalAPIServiceTest {
// Mock class for HTTP callouts
private class MockHttpResponse implements HttpCalloutMock {
public HttpResponse respond(HttpRequest req) {
HttpResponse res = new HttpResponse();
res.setStatusCode(200);
res.setBody('{"success": true, "data": {"id": "12345"}}');
return res;
}
}
@IsTest
static void testCallExternalAPI_Success() {
// Given
Test.setMock(HttpCalloutMock.class, new MockHttpResponse());
// When
Test.startTest();
String result = ExternalAPIService.callAPI('test-endpoint');
Test.stopTest();
// Then
Assert.isTrue(result.contains('success'), 'Response should indicate success');
}
}
Pattern 4: Test Data Factory
Use template: templates/test-data-factory.cls
@IsTest
public class TestDataFactory {
public static List<Account> createAccounts(Integer count) {
List<Account> accounts = new List<Account>();
for (Integer i = 0; i < count; i++) {
accounts.add(new Account(
Name = 'Test Account ' + i,
Industry = 'Technology',
BillingCity = 'San Francisco'
));
}
return accounts;
}
public static List<Contact> createContacts(Integer count, Id accountId) {
List<Contact> contacts = new List<Contact>();
for (Integer i = 0; i < count; i++) {
contacts.add(new Contact(
FirstName = 'Test',
LastName = 'Contact ' + i,
AccountId = accountId,
Email = 'test' + i + '@example.com'
));
}
return contacts;
}
// Convenience method with insert
public static List<Account> createAndInsertAccounts(Integer count) {
List<Account> accounts = createAccounts(count);
insert accounts;
return accounts;
}
}
Agentic Test-Fix Loop Implementation
How It Works
When the agentic loop is enabled, sf-testing will:
- Run tests and capture results
- Parse failures to identify error type and location
- Read source files (test class + class under test)
- Analyze root cause using the decision tree above
- Generate fix by invoking sf-apex skill
- Re-run failing test to verify fix
- Iterate until passing or max attempts (3)
Example Agentic Flow
User: "Run tests for AccountService with auto-fix enabled"
Claude:
1. sf apex run test --class-names AccountServiceTest --code-coverage --result-format json
2. Parse results: 1 failure - testBulkInsert line 45 NullPointerException
3. Read AccountServiceTest.cls (line 45 context)
4. Read AccountService.cls (trace the null reference)
5. Identify: Missing null check in AccountService.processAccounts()
6. Skill(sf-apex): Add null safety to AccountService.processAccounts()
7. Deploy fix
8. Re-run: sf apex run test --tests AccountServiceTest.testBulkInsert
9. ✅ Passing! Report success.
Cross-Skill Integration
| Skill | When to Use | Example |
|---|---|---|
| sf-apex | Generate test classes, fix failing code | Skill(skill="sf-apex", args="Create test class for LeadService") |
| sf-data | Create bulk test data (251+ records) | Skill(skill="sf-data", args="Create 251 Leads for bulk testing") |
| sf-deploy | Deploy test classes to org | Skill(skill="sf-deploy", args="Deploy tests to sandbox") |
| sf-debug | Analyze failures with debug logs | Skill(skill="sf-debug", args="Analyze test failure logs") |
Common Test Failures & Fixes
| Failure | Likely Cause | Fix |
|---|---|---|
MIXED_DML_OPERATION |
User + non-setup object in same transaction | Use System.runAs() or separate transactions |
CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY |
Trigger or flow error | Check trigger logic with debug logs |
REQUIRED_FIELD_MISSING |
Test data incomplete | Add required fields to TestDataFactory |
DUPLICATE_VALUE |
Unique field conflict | Use dynamic values or delete existing |
FIELD_CUSTOM_VALIDATION_EXCEPTION |
Validation rule fired | Meet validation criteria in test data |
UNABLE_TO_LOCK_ROW |
Record lock conflict | Use FOR UPDATE or retry logic |
Dependencies
Required: Target org with sf CLI authenticated
Recommended: sf-apex (for auto-fix), sf-data (for bulk test data), sf-debug (for log analysis)
Install: /plugin install github:Jaganpro/sf-skills/sf-testing
Documentation
| Document | Description |
|---|---|
| testing-best-practices.md | General testing guidelines |
| cli-commands.md | SF CLI test commands |
| mocking-patterns.md | Mocking vs Stubbing, DML mocking, HttpCalloutMock |
| performance-optimization.md | Fast tests, reduce execution time |
Templates
| Template | Description |
|---|---|
| basic-test.cls | Standard test class with Given-When-Then |
| bulk-test.cls | 251+ record bulk testing |
| mock-callout-test.cls | HTTP callout mocking |
| test-data-factory.cls | Reusable test data creation |
| dml-mock.cls | DML abstraction for 35x faster tests |
| stub-provider-example.cls | StubProvider for dynamic behavior |
Credits
See CREDITS.md for acknowledgments of community resources that shaped this skill.
License
MIT License. See LICENSE file. Copyright (c) 2024-2025 Jag Valaiyapathy