Read more at, or return to the homepage

Introduction & Testing Philosophy

Whether you're a seasoned vet or somebody just getting into Apex development, it's my hope that this instructional series will prove fruitful for you. But a little background, first — I've been developing on the (SFDC) ecosystem for more than 5 years, and in that time I went from working on an enormous project, with tens of millions of accounts, to a greenfield project that gradually expanded, to consulting for a variety of small to medium-sized companies.

Developing Salesforce's Apex code — itself, an interesting subset of Java — leads to many fun and interesting design decisions. Java is not the tersest programming language in the world, and while Apex does offer some extremely helpful shortcuts, it doesn't support lambda functions, and you'll need to jump through hoops to do any kind of "functions as first class citizens" argument passing, as well. With that being said, what Apex does offer:

Take a look at that last bullet point: required code coverage. This is one of the hallmark characteristics of doing great development on the SFDC platform - writing strict unit tests that ensure your code is doing exactly what the business expects it to do. Indeed, because code coverage of over 75% is required in order to do production level deploys, Salesforce implicitly (and explicitly in its Trailhead articles) encourages Test Driven Development (or TDD).

linkWhat is Test Driven Development?

Test Driven Development means, simply, that you don't write production level code till you have written a failing test asserting the behavior that you'd like to have happen.

A simple example might be structured like a user story:

As a person in X department, I expect that when I save a phone number to an account, the phone number will be formatted.

In Apex, that might look something like:

1link//in a test file annotated with @isTest at the top:

2linkAccount newAccount = new Account(Phone = '1234567890');

3linkinsert newAccount;


5link//change the reference for the Account by re-querying for it to observe

6link//whether or not the phone number has changed

7link//System.assertEquals(expected, actual) is one of many helper functions provided by SFDC

8linknewAccount = [SELECT Phone FROM Account WHERE Id = :newAccount.Id];

9linkSystem.assertEquals('123-456-7890', newAccount.Phone);

Without any custom code, this test will fail. That's great! Failure is your starting point in TDD - and I think that's really quite powerful. We don't start successful - we achieve success by building our way there. TDD is not just a software development philosophy, but a way to approach life humbly.

To close the example, an EXTREMELY simple and totally jury-rigged way of getting your test to pass:

1link//assuming you have a trigger written for Account

2link//(more on that later) that references this code:


4linkpublic void beforeInsert(List<SObject> newRecords) {

5link List<Account> newAccounts = (List<Account>) newRecords;

6link //...processing code here

7link this.formatPhoneNumbers(newAccounts);



10linkprivate void formatPhoneNumbers(List<Account> newAccounts) {

11link //regex patterns are supported in Apex, but they are cast to strings

12link String nonDigits = '[^0-9]';

13link for(Account account : newAccounts) {

14link if(String.isNotBlank(account.Phone)) {

15link String onlyDigits = account.Phone.replace(nonDigits, '');

16link if(onlyDigits.length() == 10) {

17link account.Phone = '(' + onlyDigits.substring(0, 3) + ') '

18link + onlyDigits.substring(3, 6) + '-'

19link + onlyDigits.substring(6);

20link } else if (onlyDigits.length() == 11 && onlyDigits.substring(0, 1) == '1') {

21link account.Phone = + onlyDigits.substring(1, 4) + ') '

22link + onlyDigits.substring(4, 7) + '-'

23link + onlyDigits.substring(7, 11);

24link }

25link }

26link }



Remember: this is fast and dirty. I don't recommend deeply nested if statements in Apex, this code doesn't address obvious discrepancies (like the one presented by our test!) in terms of dealing with fake numbers, but despite the example's contrivance, you can well imagine how quickly a code base can become entangled in the pursuit of testing excellence if things start to look like this.

linkWriting great tests

Writing great tests is not only the hallmark of a succesful Salesforce project, it's satisfying in the extreme. The principles I'm going to teach you can be applied to improve the code quality of an existing codebase, quality of life for you or your developers, and the speed with which a new project gets off the ground. I mention quality of life, in particular, because retaining good talent is one of the principle challenges employers deal with, and nothing impacts the velocity of a project quicker than unhappy or unmotivated developers. A clean code base in Apex exhibits several characteristics which make working in it fun:

A good example of how to balance abstraction and implementation is when writing tests: "Don't Repeat Yourself" (DRY) is a motto oft-uttered by developers, but it can be taken to an extreme. When writing Apex unit tests, in particular, it's easy to abstract away the setup of all your Salesforce objects (or SObjects) into helper functions, which can then take a variety of arguments to properly customize how the test is setup. In a code base where you are often making changes to the same few files, good helper functions for SObject creation are essential - but it's easy to take them too far. Don't fall into the trap of making the tests themselves unreadable by abstracting away so much of the essence of the test that, when new functionality is introduced and old tests break, make debugging the tests into a nightmare.

linkWriting FAST Apex Unit Tests

The first SFDC project I worked on turned into a nightmare. It's a familiar story: a greenfield project got the go ahead, without a hard deadline, to sunset a legacy CRM system. This is Salesforce's bread and butter, eating into homegrown CRM nightmares and Oracle's marketshare alike by offering out of the box alternatives with easy setup. But then the user requirement gathering begins, the backlog begins to grow, and the team starts to struggle to continue to produce quality code given a deadline that suddenly materializes halfway through the project's beginnings. As new functionality is introduced at breakneck speed, existing unit tests start breaking. Small disruptions to forward progress turn into half-day headscratchers as the team tries to fix the broken tests. Things start getting commented out - after all, code coverage is high enough elsewhere to allow the deploys to continue. Code quality starts to suffer, with confusing conditonals and feature-flagged additions piling on.

The breaking point at my first job came when the flaky tests started ruining deployments that had gone from minutes to hours. There's nothing more heartbreaking than failing to deploy after watching the tests run for a few hours due to a flaky test. I walked away from that job confused and more than a little heartbroken - I'd poured more than two years of my life into a system that barely worked. What had gone wrong?

As I said, TDD starts with you failing. I recognized that my previous team's biggest issue boiled down to how long it took to deploy - and how long it took for our tests to run. Much of the lag time for running the tests was occurring because our custom objects were so branched from the standard Salesforce SObjects that it took many database inserts and updates in order to get objects into a workable state for our tests. It got me to wondering ... how could I structure the tests on my next project so that our test time never suffered? Was such a thing possible?

There were four problems with my first SFDC project:

I decided to tackle the process of fixing the test time first. I was on a new project and sold my team the notion that our test time was going to be critical to ensure we could continually deliver. I went back to the drawing board with a friend of mine, Jonathan Gillespie, and we began to envision a way to mock the SFDC database in tests. The results were eye-popping! A properly setup environment - which took only a bit of boilerplate - could properly serve up production-ready code while allowing for objects to be easily mocked in our tests.

In order to simulate high testing loads produced by hundreds of tests running in parallel, we simulated the effects of the old tests by copying some simple tests with many updates (for example, properly setting up some OpportunityContactRole objects ...). Without a mocked database layer, these copied tests took about 45 minutes to run properly.

With the mocking framework in place, test times were reduced to mere minutes.

linkHow we did it

How did we get such a drastic reduction in test time? We found all the instances where DML statements like:

1linkinsert newAccounts;

2linkupdate newAccounts;



5link//and the most heinous statement of all:


Were used, and we replaced them with statements like this (more on this in the Mocking DML post):

1link//in practice, these aren't static classes

2link//objects requiring DML have a Crud / LeadConverter

3link//passed to them


5link//insert / update / delete / convert are reserved words in Apex:






We also replaced all raw SOQL statements with an object, Repository that handles all database queries, is easily extended per object, and is easily replaced in tests through its interface. But I'm getting a bit ahead of myself. For now I'll wrap this up.

linkMake Apex Unit Testing a Joy

TDD can either make or break a Salesforce project. Practicing it correctly - and using a testing framework that enforces good traditions and speedy tests - is paramount. I plan to cover in the coming months more info on how to set yourself up for success by explaining my testing framework through detailed code excerpts and fun examples.

If you enjoyed this introduction to the Joys of Apex, I'd love to hear from you], even a one-line note. Many thanks if you made it this far!

The original version of The Joys Of Apex: Intro can be read on my blog.

What is Test Driven Development?Writing great testsWriting FAST Apex Unit TestsHow we did itMake Apex Unit Testing a Joy

Home Advanced Logging Using Nebula Logger Apex Logging Service Apex Object-Oriented Basics Batchable And Queueable Apex Building A Better Singleton Continuous Integration With SFDX Dependency Injection & Factory Pattern Enum Apex Class Gotchas Extendable Apis Formula Date Issues Future Methods, Callouts & Callbacks Idiomatic Salesforce Apex Introduction & Testing Philosophy Lazy Iterators Lightweight Trigger Handler LWC Composable Modal LWC Composable Pagination LWC Custom Lead Path Mocking DML React Versus Lightning Web Components Refactoring Tips & Tricks Replacing DLRS With Custom Rollup Repository Pattern setTimeout & Implementing Delays Sorting And Performance In Apex Test Driven Development Example Testing Custom Permissions The Tao Of Apex Writing Performant Apex Tests

Read more tech articles