Skip to content
Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
DictionaryTTest Class
DevelopmentIntermediate

Test Class

A test class in Salesforce is an Apex class written to confirm that your custom code behaves the way you expect.

§ 01

Definition

A test class in Salesforce is an Apex class written to confirm that your custom code behaves the way you expect. You mark it with the @isTest annotation, populate it with sample data, run the logic under test, and check the results with assertions. It exercises triggers, classes, controllers, and batch jobs without touching real production records.

Salesforce treats test classes as a platform requirement, not an optional extra. Before any Apex deploys to production or ships in a managed package, at least 75% of your Apex lines must be covered by passing tests, and every trigger needs some coverage. That rule is why test classes sit at the center of the Salesforce development lifecycle.

§ 02

How test classes work and why the platform demands them

The @isTest annotation and what it changes

A class becomes a test class the moment you add the @isTest annotation above the class declaration. That annotation does real work. Code inside an @isTest class does not count against your org's Apex character limit, so a large test suite never eats into the space available for production logic. Test methods inside the class are also marked, either with @isTest on each method or with the older testMethod keyword. A class annotated with @isTest must be a top-level class, never an inner class. You can declare a test class as private or public. If the class exists only to test other code, declare it private, since nothing else needs to call it. By default a test class cannot see the records already in your org. The setting that controls this is SeeAllData, and it defaults to false. That default forces each test to build its own data, which keeps tests independent and repeatable across a sandbox, a scratch org, or production. You can set SeeAllData=true to read existing data, but Salesforce discourages it because the test then depends on records that may differ between environments.

The 75% coverage rule and why it exists

The headline number every Salesforce developer knows is 75%. At least three-quarters of your Apex lines must run during tests before the code can deploy to production or be packaged, and all tests must pass. Each trigger needs at least some coverage on top of that. The platform enforces this because Salesforce is multi-tenant. Your custom code runs on infrastructure shared with thousands of other organizations, so Salesforce needs confidence that an upgrade will not silently break your logic. Passing tests give that confidence at every release. Treat 75% as a floor, not a target. Salesforce's own guidance is to aim much higher, because coverage alone is a weak signal. A test can execute a line without checking that the line did anything useful. The real goal is meaningful coverage, where every important branch runs and every outcome is verified. Coverage often stalls below 100% when conditional code has branches that your data never triggers, so vary your test data to push execution down both sides of each if statement.

Assertions are the part that actually tests

Coverage measures which lines ran. Assertions measure whether those lines were correct, and that distinction is the difference between a real test and a coverage filler. The Assert class and the older System.assertEquals, System.assertNotEquals, and System.assert methods let you state what the result should be. If the actual value differs, the test fails and tells you exactly where. A common anti-pattern is a test that calls a method, hits 90% coverage, and never checks a single value. It will pass even after a bug is introduced, because nothing verifies the output. Good tests assert on the things that matter: the field values after a trigger fires, the size of a returned list, the error thrown for invalid input, the status of a record after an update. Write assertions for the happy path and for the failure path. When you expect an exception, wrap the call in try-catch, then assert that the catch block actually ran with the message you expected. Coverage without assertions proves your code can run, not that it works.

Test.startTest and Test.stopTest

Inside a test method you can call Test.startTest() and Test.stopTest() once each, and the pair earns its place for two reasons. First, the code between them runs with a fresh set of governor limits, separate from the limits consumed by your data setup. That separation lets you verify your code behaves within its own limits even when building the test data was expensive. Put all your record creation before Test.startTest(), then call the method under test inside the block. Second, Test.stopTest() forces queued asynchronous work to finish synchronously. Any future method, queueable, batch job, or scheduled job that your code enqueues runs to completion the instant stopTest() is reached. That is the only reliable way to assert on the results of asynchronous Apex inside a test. Without the pair, an async job stays queued and your assertions run before it ever executes, so they check nothing. This makes the startTest and stopTest block the standard place to test triggers that fire async logic, batch classes, and any code that schedules work for later.

Test data, bulk testing, and isolation

Because SeeAllData defaults to false, every test builds the data it needs. Doing this inline in each method gets repetitive fast, so two tools help. A @testSetup method runs once before the test methods in the class and inserts shared records that every method can query, which keeps setup in one place and speeds the suite up. Many teams also build a reusable Test Data Factory, a utility class with methods that return ready-made accounts, contacts, and other records so individual tests stay short and readable. Bulk testing matters as much as the data factory. Salesforce processes records in batches, and code that works on one record can blow a governor limit on two hundred. So insert a list of records, often 200, and run your logic against the whole set in one transaction. This catches queries or DML placed inside loops, the classic cause of limit failures in production. Never hardcode record IDs, because an ID valid in one org is meaningless in another. Create the records the test needs and read their IDs back instead.

Mocking callouts and the Tooling API view

Tests cannot make real HTTP callouts, so any code that calls an external service needs a mock. You implement the HttpCalloutMock interface, build a fake HttpResponse, and register it with Test.setMock() before the callout runs. From then on your code receives the canned response instead of reaching the network, which keeps tests fast, deterministic, and independent of any external system being online. For more involved scenarios the Stub API lets you build mock objects of your own Apex classes. Once tests exist, you run them from the Apex Test Execution page in Setup, from the Developer Console, from VS Code with the Salesforce Extensions, or through the Tooling API and CI pipelines. Each run records which lines were covered and surfaces failures with their stack traces. Coverage is calculated org-wide across all test runs, so the 75% figure reflects your whole codebase, not a single class. Reviewing results after every change, and before every deployment, is how teams catch regressions before customers do.

§ 03

How to write a basic Apex test class

Here is the shape of a basic Apex test class. Create it in a sandbox or scratch org, point it at the class or trigger you want to cover, and run it before you deploy. The same structure scales from a single unit test to a full suite.

  1. Create the class with @isTest

    In the Developer Console or VS Code, create a new Apex class. Add the @isTest annotation above the class declaration and declare it private, since a pure test class does not need to be called from anywhere else.

  2. Build test data

    Inside a @testSetup method or at the top of the test method, insert the records your code needs. Create them in the test rather than relying on existing org data, and insert a list of around 200 records when you want to confirm bulk behavior.

  3. Run the code inside startTest and stopTest

    Call Test.startTest(), then invoke the method or trigger you are testing, then call Test.stopTest(). This gives your code a fresh set of governor limits and forces any queued asynchronous work to finish before you check results.

  4. Assert the outcomes

    After stopTest(), query the records back and use Assert or System.assertEquals to confirm the values are what you expect. Cover both a valid case and an invalid one so the failure path is tested too.

  5. Run and check coverage

    Execute the test from the Apex Test Execution page or the Developer Console. Confirm it passes and review the coverage it produced, aiming well above the 75% minimum before you deploy.

@isTest annotationrequired

Placed above the class declaration to mark it as a test class so its code is excluded from the Apex character limit.

Test methodrequired

At least one method marked @isTest (or the legacy testMethod keyword) that contains the actual test logic.

Test datarequired

Records created inside the test, since SeeAllData is false by default and the test cannot see existing org data.

Assertionsrequired

Assert or System.assertEquals calls that verify the results, without which the test only adds coverage and checks nothing.

Gotchas
  • Coverage without assertions passes even when the code is broken. Always verify outcomes, not just that lines ran.
  • Hardcoded record IDs fail in other orgs. Create records in the test and read their IDs back instead.
  • Asynchronous work stays queued unless Test.stopTest() runs, so assertions placed before it will check nothing.
  • Real HTTP callouts are blocked in tests. Register a mock with Test.setMock() or the test throws an exception.
  • Setting SeeAllData=true makes the test depend on org records that may differ between environments, so avoid it.

Prefer this walkthrough as its own page? How to Test Class in Salesforce, step by step

§

Trust & references

Sources

Cross-checked against the following references.

Official documentation

Straight from the source - Salesforce's reference material on Test Class.

Was this entry helpful?
Help us write better definitions. Quick reactions or detailed edit suggestions.

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 minimum code coverage does a Test Class help satisfy to deploy Apex to production?

Q2. Why should a Test Class include System.assert statements rather than only executing code?

Q3. What does the @isTest(SeeAllData=false) annotation do for a Test Class?

§

Discussion

Loading…

Loading discussion…