PluginBench
Skill
Pass
Audit score 90

platform-apex-test-generate

forcedotcom/sf-skills

Generate and validate Apex test classes with TestDataFactory patterns, bulk testing, mocking, and coverage analysis.

What is platform-apex-test-generate?

Generates production-ready Apex test classes following Salesforce best practices, including TestDataFactory patterns, bulk testing (251+ records), mocking, assertions, and disciplined test-fix loops. Use when creating or improving Apex test classes for triggers, services, controllers, batch jobs, queueables, and callouts, or when debugging failing tests and analyzing coverage.

  • Generate test classes with one behavior per method and separate positive, negative, and bulk test scenarios
  • Create TestDataFactory classes for isolated, reusable test data setup
  • Implement bulkified tests with 251+ records to cross the 200-record trigger batch boundary
  • Apply mocking patterns for external boundaries (HttpCalloutMock, SOSL, DML isolation)
  • Run disciplined test-fix loops with coverage analysis and focused debugging
  • Validate assertions with exact expected values and meaningful failure messages

How to install platform-apex-test-generate

npx skills add https://github.com/forcedotcom/sf-skills --skill platform-apex-test-generate
Prerequisites
  • Salesforce CLI (sf) version 2.0.0 or higher
  • API version 66.0 or higher in org
  • Existing production Apex classes to test (triggers, services, controllers, batch jobs, queueables, callouts)
Claude Code
Cursor
Windsurf
Cline

How to use platform-apex-test-generate

  1. 1.Gather context: identify target production class(es), existing test classes, test data factories, and coverage threshold
  2. 2.Generate test class using the template structure with Given/When/Then pattern and one behavior per method
  3. 3.Create TestDataFactory class if none exists to handle isolated test data setup
  4. 4.Run focused tests with sf apex run test command against target org
  5. 5.Analyze results for failing methods, uncovered lines, and weak coverage areas
  6. 6.Execute disciplined fix loop (max 3 iterations) to address test failures or coverage gaps

Use cases

Good for
  • Creating new Apex test classes for triggers, services, controllers, batch jobs, queueables, and callouts
  • Improving code coverage from baseline to 75% minimum or 90%+ recommended targets
  • Debugging and fixing failing Apex tests with root-cause analysis and iterative fixes
  • Analyzing coverage reports to identify uncovered lines and weak coverage areas
  • Testing async operations (batch Apex, queueables, future methods) with proper Test.startTest/stopTest wrapping
Who it's for
  • Salesforce developers writing Apex test classes
  • QA engineers validating Apex code coverage and test quality
  • Platform engineers maintaining trigger and service test suites
  • Developers improving test coverage before production deployments

platform-apex-test-generate FAQ

When should I use this skill vs. platform-apex-generate?

Use platform-apex-test-generate for Apex test classes (*Test.cls files), test data factories, and coverage analysis. Use platform-apex-generate for production Apex code (triggers, services, controllers, batch jobs). Do NOT use this skill for Jest/LWC tests.

What is the minimum code coverage required?

Salesforce requires 75% minimum coverage for production deployment. Best practice is 90%+ coverage, with 100% coverage for business-critical code paths.

How do I test Batch Apex in test context?

In test context, only one execute() invocation runs. Set batchSize >= testRecordCount and use Test.startTest/stopTest to force synchronous execution. See references/async-testing.md for details.

Should I use System.assert or Assert class?

Use the Assert class only (Assert.areEqual, Assert.isTrue, Assert.fail, etc.). Never use legacy System.assert, System.assertEquals, or System.assertNotEquals.

How do I handle external callouts in tests?

Use HttpCalloutMock to mock external boundaries. Design production code for testability via constructor injection. See references/mocking-patterns.md for patterns.

Full instructions (SKILL.md)

Source of truth, from forcedotcom/sf-skills.


name: platform-apex-test-generate description: "Use to generate and validate Apex test classes with TestDataFactory patterns, bulk testing (251+ records), mocking, assertions, and disciplined test-fix loops. Use when creating Apex test classes (for triggers, services, controllers, batch jobs, queueables, and callouts), improving coverage, debugging or fixing failing Apex tests, or running test/coverage analysis. Triggers on *Test.cls files, sf apex run test, coverage reports. Do NOT trigger for production Apex (use platform-apex-generate) or Jest/LWC tests." metadata: version: "1.1" domains: ["Platform"] minApiVersion: "66.0" relatedSkills: - "platform-apex-generate" cliTools: - tool: ["sf"] semver: ">=2.0.0"

Generating Apex Tests

Generate production-ready Apex test classes and run disciplined test-fix loops with coverage analysis.

Core Principles

  1. One behavior per method — each test method validates a single scenario. Separate positive, negative, and bulk tests. NEVER combine related-but-distinct inputs (e.g., null and empty) in one method — create _NullInput_ and _EmptyInput_ as separate test methods
  2. Bulkify tests — test with 251+ records to cross the 200-record trigger batch boundary. Batch Apex exception: in test context only one execute() invocation runs, so set batchSize >= testRecordCount. See references/async-testing.md
  3. Isolate test data — every @TestSetup must delegate record creation to a TestDataFactory class. If none exists, create one first. Never build record lists inline in @TestSetup. Never rely on org data (SeeAllData=false) or hardcoded IDs. For duplicate rule handling, see references/test-data-factory.md
  4. Assert meaningfully — use exact expected values computed from test data setup. NEVER use range assertions or approximate counts when the value is deterministic. Always include failure messages. See references/assertion-patterns.md
  5. Use Assert class only — Assert.areEqual, Assert.isTrue, Assert.fail, etc. Never use legacy System.assert, System.assertEquals, or System.assertNotEquals
  6. Mock external boundaries — use HttpCalloutMock for callouts, Test.setFixedSearchResults for SOSL, DML mock classes for database isolation. Design for testability via constructor injection. See references/mocking-patterns.md
  7. Test negative paths — validate error handling and exception scenarios, not just happy paths
  8. Wrap with start/stop — pair Test.startTest() with Test.stopTest() to reset governor limits and force async execution

Test.startTest() / Test.stopTest()

Always wrap the code under test in Test.startTest() / Test.stopTest():

  • Resets governor limits so the test measures only the code under test
  • Executes async operations synchronously (queueables, batch, future methods)
  • Fires scheduled jobs immediately

Test Code Anti-Patterns

Anti-PatternFix
SOQL/DML inside loopsQuery once before the loop; use Map<Id, SObject> for lookups
Magic numbers in assertionsDerive expected values from setup constants
God test class (>500 lines)Split into multiple test classes by behavior area
Long test methods (>30 lines)Extract Given/When/Then into helper methods
Generic Exception catchCatch the specific expected type (e.g., DmlException)

Workflow

Step 1 — Gather Context

Before generating or fixing tests, identify:

  • the target production class(es) under test
  • existing test classes, test data factories, and setup helpers
  • desired test scope (single class, specific methods, suite, or local tests)
  • coverage threshold (75% minimum for deploy, 90%+ recommended)
  • org alias when running tests against an org

Step 2 — Generate the Test Class

Apply the structure, naming conventions, and patterns from the asset templates and reference docs.

MANDATORY — File Deliverables: For every test class, create BOTH files:

  1. {ClassName}Test.cls — the test class (use assets/test-class-template.cls as starting point)
  2. {ClassName}Test.cls-meta.xml — the metadata file:
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>66.0</apiVersion>
    <status>Active</status>
</ApexClass>

If no TestDataFactory exists in the project, create TestDataFactory.cls + TestDataFactory.cls-meta.xml using assets/test-data-factory-template.cls.

@TestSetup Example

@TestSetup
static void setupTestData() {
    List<Account> accounts = TestDataFactory.createAccounts(251, true);
}

Test Method Structure

Use Given/When/Then:

@isTest
static void shouldUpdateStatus_WhenValidInput() {
    // Given
    List<Account> accounts = [SELECT Id FROM Account];

    // When
    Test.startTest();
    MyService.processAccounts(accounts);
    Test.stopTest();

    // Then
    List<Account> updated = [SELECT Id, Status__c FROM Account];
    Assert.areEqual(251, updated.size(), 'All accounts should be processed');
}

Negative Test — Exception Pattern

Use try/catch with Assert.fail to verify expected exceptions:

@isTest
static void shouldThrowException_WhenInvalidInput() {
    // Given
    List<Account> emptyList = new List<Account>();

    // When/Then
    Test.startTest();
    try {
        MyService.processAccounts(emptyList);
        Assert.fail('Expected MyCustomException to be thrown');
    } catch (MyCustomException e) {
        Assert.isTrue(e.getMessage().contains('cannot be empty'),
            'Exception message should indicate empty input');
    }
    Test.stopTest();
}

Naming Convention

  • should[ExpectedResult]_When[Scenario]: shouldSendNotification_WhenOpportunityClosedWon
  • [SubjectOrAction]_[Scenario]_[ExpectedResult]: AccountUpdate_ChangeName_Success

Step 3 — Run Tests

Start narrow when debugging; widen after the fix is stable.

# Single test class
sf apex run test --class-names MyServiceTest --result-format human --code-coverage --target-org <alias>

# Specific test methods
sf apex run test --tests MyServiceTest.shouldUpdateStatus_WhenValidInput --result-format human --target-org <alias>

# All local tests
sf apex run test --test-level RunLocalTests --result-format human --code-coverage --target-org <alias>

Step 4 — Analyze Results

Focus on:

  • failing methods — exception types and stack traces
  • uncovered lines and weak coverage areas
  • whether failures indicate bad test data, brittle assertions, or broken production logic
  • when class metadata crosses from versions 66.0 and below to 67.0 and above, check explicit sharing, decide user/system mode per operation, grant required CRUD/FLS in test users or permission sets, and rerun affected tests

Step 5 — Fix Loop

When tests fail, run a disciplined fix loop (max 3 iterations — stop and surface root cause if still failing):

  1. Read the failing test class and the class under test
  2. Identify root cause from error messages and stack traces
  3. Apply fix — adjust test data or assertions for test-side issues; delegate production code issues to the platform-apex-generate skill
  4. Rerun the focused test before broader regression
  5. Repeat until all tests pass, iteration limit reached, or root cause requires design change

Step 6 — Validate Coverage

LevelCoveragePurpose
Production deploy75% minimumRequired by Salesforce
Recommended90%+Best practice target
Critical paths100%Business-critical code

Cover all paths: positive, negative/exception, bulk (251+ records), callout/async.

What to Test by Component

ComponentKey Test Scenarios
TriggerBulk insert/update/delete, recursion guard, field change detection
ServiceValid/invalid inputs, bulk operations, exception handling
ControllerPage load, action methods, view state
Batchstart/execute/finish, scope matching (batch size >= record count), Database.Stateful tracking, error handling, chaining (separate methods — finish() calling Database.executeBatch() throws UnexpectedException)
QueueableChaining (only first job runs in tests), bulkification, error handling, callout mocks before Test.startTest()
CalloutSuccess response, error response, timeout
SelectorValid/null/empty inputs, bulk (251+), field population, sort order, WITH USER_MODE via System.runAs
ScheduledDirect execution via execute(null), CRON registration via CronTrigger query
Platform EventTest.enableChangeDataCapture(), Test.getEventBus().deliver(), verify subscriber side effects

Output Expectations

Deliverables per test class:

  • {ClassName}Test.cls + {ClassName}Test.cls-meta.xml (match API version of class under test; default 66.0)
  • TestDataFactory.cls + TestDataFactory.cls-meta.xml (if not already present)

Reference Files

Load on demand for detailed patterns:

ReferenceWhen to use
references/test-data-factory.mdTestDataFactory patterns, field overrides, duplicate rule handling
references/assertion-patterns.mdAssertion best practices, anti-patterns, common pitfalls
references/mocking-patterns.mdHttpCalloutMock, DML mocking, StubProvider, SOSL, Email, Platform Events
references/async-testing.mdBatch, Queueable, Future, Scheduled job testing