Testing, Debugging, and Deployment · Topic 1 of 4

Testing Triggers, Classes, Controllers and Flows

Apex testing for Platform Developer: test classes, test data, TestSetup, startTest and stopTest, runAs, callout mocks, coverage rules and flow tests.

  • 4 min
In a nutshell
  • Production deploys need 75% overall coverage, coverage on every trigger, and all tests passing.
  • Tests do not see org data by default; create it, ideally in @TestSetup or a factory.
  • startTest() gives fresh limits; stopTest() runs queued async work.

What it is

Apex unit tests are methods that run your code and assert that it behaves as expected. Salesforce requires tests to deploy Apex to production: at least 75% overall code coverage, every trigger with some coverage, and every test passing.

Tests run in isolation. They create their own data, and everything they write is rolled back when the test ends.

Test setupcreate datastartTestfresh limitsRun codestopTestasync runsAssert results

How it works

A test class

@IsTest
private class ContactTriggerTest {
    @TestSetup
    static void makeData() {
        insert new Account(Name = 'Acme');
    }
    @IsTest
    static void setsDescriptionInBulk() {
        Account a = [SELECT Id FROM Account LIMIT 1];
        List<Contact> cs = new List<Contact>();
        for (Integer i = 0; i < 200; i++) {
            cs.add(new Contact(LastName = 'C' + i, AccountId = a.Id));
        }
        Test.startTest();
        insert cs;
        Test.stopTest();
        for (Contact c : [SELECT Description FROM Contact]) {
            Assert.areEqual('Acme', c.Description);
        }
    }
}

Key features

FeatureWhat it does
@IsTestMarks a test class or method; test code does not count toward the org's Apex size limit
@TestSetupCreates data once for all test methods in the class; each method sees a fresh copy
Test.startTest() / Test.stopTest()Code between them gets a fresh set of governor limits; at stopTest() asynchronous work (future, queueable, batch) runs synchronously
System.runAs(user)Runs code as a given user to test sharing and permissions; also avoids mixed DML errors in tests
Assert.areEqual, Assert.isTrue, System.assertEqualsCheck results
Test.setMock(HttpCalloutMock.class, mock)Return fake HTTP responses; tests cannot make real callouts
Test.loadDataLoad test records from a CSV static resource
@IsTest(SeeAllData=true)Lets the test see org data; avoid it, because results depend on the org

By default, tests cannot see existing org data (except some setup data such as users and profiles). A query for Accounts in a test with no setup returns nothing.

Testing different code

CodeApproach
TriggersInsert, update or delete records; test 1 record, 200 records and invalid data
Controllers and extensionsTest.setCurrentPage(Page.MyPage), set parameters with ApexPages.currentPage().getParameters().put(...), construct the controller and call its actions
Batch and queueableStart them between startTest() and stopTest(), then assert
CalloutsA class implementing HttpCalloutMock; branch on req.getEndpoint() to mock several endpoints
Permission-sensitive codeSystem.runAs() with a user who lacks access
FlowsFlow tests in Flow Builder for record-triggered flows, or Apex tests that perform the DML that triggers the flow
Lightning Web ComponentsJest tests in the local project; not part of Apex coverage

Test data factory

A public @IsTest utility class with static methods such as createAccounts(Integer n) gives every test consistent, valid data and keeps setup in one place.

Coverage arithmetic

Coverage counts executable lines only; comments and blank lines are ignored. If a class has 70 executable lines and tests run 56 of them, coverage is 80%.

A worked example

A trigger rejects contacts without Email using addError(). A good test inserts 200 contacts with Database.insert(contacts, false), three without Email. It checks that the three SaveResult entries failed with the trigger's message, and that 197 contacts exist afterwards. That one test proves bulk behaviour, the rejection path and the message, which coverage alone never shows.

Exam tip

Exam answers that "increase coverage" without assertions, or use SeeAllData=true to find data, are almost always wrong. Create test data and assert results.

How the exam asks about this

  • "Async code must finish before the assertions." Wrap it in Test.startTest() and Test.stopTest().
  • "Test that a user without access sees no records." System.runAs().
  • "Code makes a callout." Test.setMock with an HttpCalloutMock.

What to remember for the exam

  • Production deploys need 75% overall coverage, coverage on every trigger, and all tests passing.
  • Tests do not see org data by default; create it, ideally in @TestSetup or a factory.
  • startTest() gives fresh limits; stopTest() runs queued async work.
  • System.runAs() tests user access and avoids mixed DML errors.
  • Mock callouts with HttpCalloutMock; real callouts are not allowed in tests.
Watch out

A test method with no assertions can reach 100% coverage and still let a broken trigger reach production. Every test needs at least one meaningful assertion.

Common mistakes

Relying on existing records. Tests without SeeAllData see no org data, and results would vary between orgs anyway.

Testing with one record only. Bulk bugs appear only with 200 records.

Asserting after an async call without stopTest(). The job has not run yet.

Check yourself

  • What does Test.stopTest() do to a queueable job enqueued after startTest()?

    It runs the job synchronously, so the test can assert its results.

  • What does [SELECT COUNT() FROM Account] return in a test with no setup data and no SeeAllData?

    Zero, because tests do not see org data by default.

  • How do you test code that makes an HTTP callout?

    Implement HttpCalloutMock and register it with Test.setMock before calling the code.

Topic 1 of 4 in Testing, Debugging, and Deployment

Test yourself on Testing, Debugging, and Deployment

The full Salesforce Platform Developer question bank has 645 questions, with timed practice exams and an explanation for every answer.

This study guide is independent and is not affiliated with, endorsed by, or sponsored by Salesforce. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.