Agent Skills: sf-testing: Salesforce Test Execution & Coverage Analysis

>

UncategorizedID: Jaganpro/sf-skills/sf-testing

Skill Files

Browse the full folder contents for sf-testing.

Download Skill

Loading file tree…

sf-testing/SKILL.md

Skill Metadata

Name
sf-testing
Description
>

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

  1. Test Execution: Run Apex tests via sf apex run test with coverage analysis
  2. Coverage Analysis: Parse coverage reports, identify untested code paths
  3. Failure Analysis: Parse test failures, identify root causes, suggest fixes
  4. Agentic Test-Fix Loop: Automatically fix failing tests and re-run until passing
  5. Test Generation: Create test classes using sf-apex patterns
  6. 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:

  1. Check existing tests: Glob: **/*Test*.cls, Glob: **/*_Test.cls
  2. Check for Test Data Factories: Glob: **/*TestDataFactory*.cls
  3. 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:

  1. Identify Uncovered Lines:
sf apex run test --class-names MyClassTest --code-coverage --detailed-coverage --result-format json --target-org [alias]
  1. 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.

  1. Bulk Test Validation:
Skill(skill="sf-data", args="Create 251 [ObjectName] records for bulk testing")
  1. 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:

  1. Run tests and capture results
  2. Parse failures to identify error type and location
  3. Read source files (test class + class under test)
  4. Analyze root cause using the decision tree above
  5. Generate fix by invoking sf-apex skill
  6. Re-run failing test to verify fix
  7. 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