Apex Unit Testing Dos and Don’ts From the Trenches

Apex is Salesforce’s programming language. It is used to create custom functionality on the Salesforce platform. To ensure that custom functionality works, Salesforce requires that 75% of the overall apex code written is covered with test code. Test code is apex code that tests the custom functionality written by a developer and doesn’t count towards the 75% of code that has to be covered.

How to Write Good Unit Tests and An Introduction to Apex Code Test Methods are good resources for how to create unit tests so that content won’t be covered here. I want to share Dos and Don’ts gained from the Apex Unit Testing trenches.

Dos

  • Isolated Data Creation Code. When writing test code, it’s common to copy and paste the same data setup code across unit tests and even test classes. This is a maintenance nightmare when your tests start numbering in the hundreds or more. To resolve this, centralize the data creation into their own classes and make them parameterized so one can supply different data as needed. While requiring more investment initially, it pays many dividends later when you reuse a lot of this logic for other tests.
  • Global Data Creation Code. When creating managed packages, it’s very common to extend and customize them for client specific functionality. With global data creation classes, the non-packaged code can easily setup various scenarios without having to re-code it. This has helped me save countless hours on various implementation projects.
  • Write Failing Unit Tests For Bugs. Once you know the reproduction steps for a bug, write a failing test for it. Once the bug is fixed, the test passes and now you have added to your regression testing suite.
  • Add Assertion Messages. In the various System.assert* methods, there is an optional message parameter that shows up for additional information when the assertion fails. While it does take a little extra time initially, it can really save time troubleshooting later when the test provides enough information that you don’t have to open up the test class or enable debug logs.
  • Test Class Naming Scheme. Test classes should have names that describe what’s being tested. For test class names, I have seen two schemes “Test<apex_class>” and “<apex_class>Test” for the naming scheme. Each one signifies that it contains the test methods for a given apex class or trigger. With the “Test<apex_class>” scheme, all the tests are grouped at the bottom of the apex class listing. With the “<apex_class>Test” scheme, the test classes are right beside the class or trigger being tested in the apex class listing. Whatever scheme you adopt, make it consistent.
  • Test Method Naming Scheme. Test methods should have names that describe what’s being tested and the expected outcome. Derek Hansen, an awesome engineer and colleague, came up with the following scheme that I really like “<method_name>_<condition>_<expected_outcome>”. For example, “insertAccount_userNotAuthorized_expectNotAuthorizedException” would test the “insertAccount” method on the class being tested for a user who doesn’t have permission to create an account and it would expect it to fail.
  • Assertion Methods. Within a given test class, its very common to assert the same condition across various test methods. Put shared assertions into their own methods and use them across the test methods. An assertion method can have one or more assertions in them and usually has more than one. This helps reduce the size of test methods and centralizes the assertion code so that changes later are only needed in one place.
  • Consider Benefit / Cost Tradeoff. Test code is an investment. While only anecdotal, I’ve found that I tend to write 1-3 lines of test code for every line of non-test code. That means that it takes at least as long to write test code for a feature as it does to code the feature.  As a result, focus your time on testing the common use cases first and then edge cases based on their frequency.

Don’ts

  • Bypass Writing Test Code. While one has to consider the benefit / cost tradeoff, that does not mean not writing test code by using “Coverage Code”, marking every class as a test class, or some other way.
  • Focusing on getting 75% code coverage being the primary metric. The unit tests are meant to confirm that the software behaves as expected so let that be your primary focus and by doing so, 75% or more of your code will more often than not naturally be covered.
  • Duplicate Code. This makes initial development fast but maintenance afterward a nightmare. This, of course, applies to any code but it seems like developers “relax” their coding best practices for test code. Please don’t forget the “refactor” step in the “Red, Green, Refactor” steps. Put differently, after copying and pasting test methods, tweaking them, getting them all to pass, refactor the code to be de-duped.
  • Put all test cases in one big test method. These are notoriously hard to troubleshoot when the test starts failing long after it was created. Create one test method per test case.

What other Dos and Don’ts do you have? Do you disagree with any listed here? Let us know in the comment below.

Happy Coding,

Luke