Unit Test
A Unit Test in Salesforce is an Apex method, annotated with @isTest, that exercises a small piece of production Apex code in isolation and asserts the expected outcome.
Definition
A Unit Test in Salesforce is an Apex method, annotated with @isTest, that exercises a small piece of production Apex code in isolation and asserts the expected outcome. The platform requires unit tests for every line of Apex deployed to production: the org-wide code coverage minimum is 75 percent, and each individual trigger needs at least 1 percent coverage. Tests run in their own transaction with their own governor limits, they cannot see real production data unless the @isTest(SeeAllData=true) annotation is used (which is discouraged), and they roll back any DML they perform at the end of the run.
Unit Tests are the gate between developer code and a successful production deploy. Salesforce executes the entire test suite during every metadata deploy to production and during package upload; a single failing test blocks the deploy. The same suite runs as part of every change set, every Salesforce DX deployment, every package upload, and every continuous integration pipeline. Mastering unit tests is therefore one of the highest-impact skills in any Salesforce development team.
Why Salesforce Apex tests work differently from generic unit tests in Java or Python
Test transaction and rollback
Every Apex test runs in its own transaction. Whatever DML the test performs (insert test Account, update test Opportunity) is rolled back at the end of the method. No real database changes survive the test run, which is what lets developers run tests against production safely. The rollback also means tests cannot share state across methods; if a test method needs setup data, the @testSetup annotation runs a setup method once per class and snapshots the data for each test method to reset against.
Code coverage requirements
Salesforce enforces a 75 percent organization-wide Apex code coverage minimum and a 1 percent per-trigger minimum at deploy time. Coverage is calculated as lines hit by test methods divided by total executable lines. Comments, blank lines, and method signatures do not count. Coverage below the threshold blocks the deploy with an error listing every class and trigger below the line. Most mature teams target 85 to 95 percent coverage in practice, with the gap reserved for branches that are genuinely hard to test (legacy generic exception handlers).
Test data isolation
Apex tests by default cannot see any production data. Every record the test reads must be created inside the test (or in the @testSetup method). The @isTest(SeeAllData=true) annotation lifts this isolation but is considered an anti-pattern: tests that depend on production data shape break whenever the data shape changes, and they cannot run reliably in a sandbox refresh. Modern test code creates a small set of fixture records, exercises the code against them, and asserts outcomes against the fixtures.
Mocking external callouts
Apex tests cannot make real HTTP callouts (the platform throws CalloutException). For code that integrates with an external API, developers implement the HttpCalloutMock interface to return canned responses, and call Test.setMock() inside the test to wire the mock in. The pattern is verbose by design: every callout in production code needs a corresponding mock in tests, which forces developers to think about contract shape and error handling for each external dependency.
Test Suites and named runs
The platform supports Test Suites: named groupings of test classes that can be executed as a unit from the Setup UI or the Apex Tooling API. A typical suite might collect all tests for the Quoting subsystem so a developer making a change there can run just those tests in seconds instead of the full multi-hour org-wide run. Test Suites also support scheduled runs through Salesforce's Apex Test Execution scheduler, giving teams a nightly regression suite without external infrastructure.
Assertions and the System.assert family
Tests verify outcomes through assertion methods: System.assertEquals(expected, actual), System.assertNotEquals, System.assert(condition), and the newer Assert class (Assert.areEqual, Assert.isTrue, Assert.isNull). A test method without assertions is technically valid (it still contributes to coverage) but adds no behavioral validation. Mature test suites pair coverage with explicit assertions on field values, related record counts, exception types thrown, and side effects produced.
Test parallelism and ordering
Apex tests run in parallel by default to speed up large suites. The platform partitions test classes across worker processes and runs them concurrently. This is fast but introduces a class of bugs: tests that rely on a specific user, a specific custom setting state, or a specific record ID can collide when two tests modify the same shared resource. The @isTest(IsParallel=false) annotation forces serial execution for tests that genuinely cannot run in parallel; most tests should be parallel-safe by design.
Write a unit test for an Apex class
Cover an existing Apex class with a unit test that creates fixture data, calls the method under test, and asserts the expected outcome.
- Create the test class
In Setup, Apex Classes, click New. Add the @isTest annotation at the class level. Name the class with a clear suffix (AccountServiceTest).
- Add a @testSetup method
Add a static method annotated @testSetup that inserts the fixture data needed by every test in the class (a few Accounts, a Contact, a User). This runs once per class and resets between methods.
- Write a test method
Add a static method annotated @isTest. Inside, query the fixture data, call the production method, and capture the returned value. The method body should read like a small story: arrange, act, assert.
- Assert the expected outcome
Use Assert.areEqual(expected, actual) or System.assertEquals to compare what happened to what should have happened. Add multiple assertions per test where multiple field updates or related-record creations matter.
- Run the test
From the Developer Console or VS Code, run the test class. Inspect the log if anything fails. Iterate until the test passes consistently.
- Verify coverage
Open the Apex Test Execution result. Confirm the target class shows expected coverage. If branches are uncovered, add additional test methods for the missing cases.
- Tests cannot make real HTTP callouts. Every callout in production code needs an HttpCalloutMock implementation; the test fails with CalloutException otherwise.
- @isTest(SeeAllData=true) breaks test isolation and is an anti-pattern. Avoid it unless you are writing a Knowledge or Email Template test that genuinely requires production-only data.
- Parallel test execution exposes data collisions. Tests that modify shared resources (custom settings, queues) need explicit @isTest(IsParallel=false) or careful design.
- Assertions without messages are hard to debug. Pass the third argument to Assert.areEqual to include a helpful failure message; the diagnosis is much faster.
Trust & references
Cross-checked against the following references.
- Apex TestingSalesforce Developer Docs
- Code CoverageSalesforce Developer Docs
- @isTest AnnotationSalesforce Developer Docs
Straight from the source - Salesforce's reference material on Unit Test.
- Apex Testing DocumentationSalesforce Developer Docs
- @testSetup AnnotationSalesforce Developer Docs
- HttpCalloutMock InterfaceSalesforce Developer Docs
Hands-on resources to go deeper on Unit Test.
About the Author
Dipojjal Chakrabarti is a B2C Solution Architect with 29 Salesforce certifications and over 13 years in the Salesforce ecosystem. He runs salesforcedictionary.com to help admins, developers, architects, and cert/interview candidates sharpen their fundamentals. More about Dipojjal.
Test your knowledge
Q1. What is a Unit Test?
Q2. What's the minimum coverage?
Q3. What makes a good unit test?
Discussion
Loading discussion…