Skip to main content

SF-9201 · Coding · Easy

How do you structure an Apex test class?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

An Apex test class is a regular class annotated @isTest whose methods exercise production code and assert the results. Salesforce requires 75% line coverage across all Apex in an org before deployment to production succeeds — but coverage is the minimum, not the goal.

The skeleton every test class follows

@isTest
private class AccountServiceTest {

    @testSetup
    static void setupData() {
        // Data shared across all test methods, created once per test class
        List<Account> accounts = new List<Account>();
        for (Integer i = 0; i < 5; i++) {
            accounts.add(new Account(Name = 'Test ' + i, Active__c = true));
        }
        insert accounts;
    }

    @isTest
    static void getActiveAccounts_returnsOnlyActive() {
        // ARRANGE — extra setup specific to this test
        insert new Account(Name = 'Inactive', Active__c = false);

        // ACT
        Test.startTest();
        List<Account> result = AccountService.getActiveAccounts(10);
        Test.stopTest();

        // ASSERT
        System.assertEquals(5, result.size(), 'Should return only active accounts');
        for (Account a : result) {
            System.assertEquals(true, a.Active__c);
        }
    }
}

The pieces, explained

  • @isTest on the class — tells the platform “this class isn’t production code; don’t count it toward the 6 MB Apex code limit; don’t deploy it unless explicitly requested.”
  • private — test classes don’t need to be public. Convention is private so they can’t be called from elsewhere.
  • @testSetup — runs once before all @isTest methods in the class. Data inserted here is automatically rolled back at the end of each test method, then restored for the next.
  • @isTest on each method — declares the test entry point. Salesforce only runs methods with this annotation.
  • static — test methods must be static.
  • No return value, no parameters — test methods are static void someTest(). Period.

Arrange — Act — Assert

The convention every senior developer follows:

  1. Arrange — create the data the code under test needs
  2. Act — call the method, wrap the call in Test.startTest() / Test.stopTest() to reset governor limits
  3. Assert — verify the result with System.assertEquals, System.assert, System.assertNotEquals

Each test method runs in its own transaction

Salesforce rolls back all DML at the end of every test method. So if Test A inserts 5 Accounts and Test B queries Account, Test B sees zero Accounts unless it inserts its own. This is why test isolation is automatic — you don’t have to clean up.

What “good coverage” looks like

The 75% bar is necessary but not sufficient. A senior reviewer looks for:

  • Positive tests — happy path
  • Negative tests — bad input, missing required fields, security exceptions
  • Bulk tests — call the method with 200 records, prove no governor limit blows up
  • Boundary tests — what happens with 0, 1, N+1, max records?

Common anti-patterns

  • Test.isRunningTest() in production code branching. Tests should exercise the real path, not a fake one.
  • No assertions — calling the method without verifying anything. The line gets covered, but the test catches nothing.
  • Hard-coding IDs — IDs differ across orgs. Query for them.

Verified against: Apex Developer Guide — Testing Apex. Last reviewed 2026-05-17 for Spring ‘26 release.