So you worked on some code and built yourself a great feature and now you have to deploy. Estimate Code Coverage and you get: Your overall code coverage is currently 74%. To deploy code to production, you must have at least 75%. No one likes to chase their tails as they are working and trying to get features and fixes out the door. Test data isolation, system vs test data entry, and trigger limitations are 3 big areas that cause problems in tests. So lets dive into some good patterns and tricks to help you stay ahead of the code coverage curve.
Isolate Your Test Data
First and probably most important is getting your test data isolated and manageable. Let’s take a look at an example of when we don’t have isolated test data created.
@isTest static private void test_AccountStuff() { Account account = New Account( Name = 'Test Account' ); insert account; system.assertNotEquals(null, account.Id); } @isTest static private void test_AccountStuff2() { Account account = New Account( Name = 'Test Account 2' ); insert account; system.assertNotEquals(null, account.Id); }
We have simple tests that will run and assert that they did indeed save into the system. But we as developers are seldom the only ones working in the system. When another administrator of the system comes along and makes a validation rule for the Account object, say requiring an AccountNumber for all Accounts, all of our tests fail. We now need to modify all test methods that deal with any account. A simple solution to this, is test data isolation.
So instead of creating the test data in the test methods themselves, let us create a class that can handle the test data creation by itself. In doing this, we will only need to update one function for any changes in validations rules, object fields, or any other changes that can impact inserting and updating test data. Like this:
@isTest static private void test_AccountStuff() { TestData testData = new TestData(); Account account = New Account( Name = 'Test Account' ); testData.createAccount(account); system.assertNotEquals(null, account.Id); }
@isTest static private void test_AccountStuff2() { TestData testData = new TestData(); Account account = New Account( Name = 'Test Account' ); testData.createAccount(account); system.assertNotEquals(null, account.Id); }
@isTest public with sharing class TestData { public Data data; public void TestData() { data = new Data(); } public TestData createAccount(Account account) { if(account.AccountNumber == null) account.AccountNumber = '123456'; insert account; this.data.accounts.add(account); return this; } public class Data { public List<Account> accounts; public void Data() { accounts = new List<Account>(); } } }
Now we can see that since we wrap our data creation in an isolated environment, we can keep the tests flexible and ensure that if we ever need to modify account test data creation, we can do it in one place. This can be expanded to handle advanced business logic test data as well as bulkify the test data creation.
If code is the heart of your application, unit testing is the stethoscope. By “hearing the heartbeat” of your code, you can quickly uncover problems while they are still small. Take your unit test code seriously by keeping the code as isolated, organized and reusable as the rest of your application code. It keep you, your team and your development and deployment processes healthy and running smooth.