Say Hello to @testSetup in Spring ’15

In Salesforce by Alex0 Comments

Anyone who has developed for the Salesforce platform knows how important testing is.

The Spring ’12 (API version 24.0) release introduced the isolation of test data from organization data in unit tests which really shook things up.

Now the Spring ’15 (API version 33.0) release is here and whilst the change introduced is not quite as revolutionary it will make a big difference to the way you write your tests.

One of the most complicated tasks when setting up your test classes in Salesforce is the creation of test data. This is normally achieve by creating utility methods which you call at the beginning of your test methods or by loading the data from CSV files.

When you only care about one object this is fairly easily. However, when you need to create multiple objects or object hierarchies with complex relationships then the start of your test methods start to get complicated and cluttered with a lot of boilerplate data setup code.

In the Spring ’15 some of that complication is being removed with the introduction of the @testSetup annotation.

The @testSetup annotation will less you designate a method to run before all of your other test methods. This is very similar to the @Before annotation in JUnit or the TestInitialize attribute in .Net.

The old way

Take this test class for example, which is the skeleton of a test class for a trigger on Opportunity records which updates the related Opportunity Line Items in some way when the Opportunity changes:

@isTest
private class OpportunityTriggerTests {
    private static void createOpportunities(...) {
        // ...
    }

    private static void createProducts(...) {
        // ...
    }

    private static void createPricebookEntries(...) {
        // ...
    }

    private static void createOpportunityLineItems(...) {
        // ...
    }

    @isTest
    private static void testMethod1() {
        createOpportunities(...);
        createProducts(...);
        createPricebookEntries(...);
        createOpportunityLineItems(...);

        // Select the Opportunities you want to manipulate out of your test data
        List<Opportunity> opportunitiesToUpdate = [SELECT Id, ... FROM Opportunity WHERE ...];

        for(Opportunity o : opportunitiesToUpdate) {
            // Make changes to each Opportunity
        }

        update opportunitiesToUpdate;

        List<OpportunityLineItem> updatedLineItems = [SELECT Id, ... FROM OpportunityLineItem WHERE ...];
        // Perform your assertions
    }

    @isTest
    private static void testMethod2() {
        createOpportunities(...);
        createProducts(...);
        createPricebookEntries(...);
        createOpportunityLineItems(...);

        // Select some data and do some testing
        // ...
    }

    @isTest
    private static void testMethod3() {
        createOpportunities(...);
        createProducts(...);
        createPricebookEntries(...);
        createOpportunityLineItems(...);

        // Select some data and do some testing
        // ...
    }
}
As you can see, each test has 4 lines of redundant code that you have to wade through each time. Whilst it may not seem like a lot, this is a relatively simple (and common) example using only standard objects.

Things can get much, much more complicated when you have lots of custom objects and relationships involved. There are also only have 3 test methods in this example, I should hope that most of your test classes have much more than that.

The @testSetup way

As a contrast, here is the same class using the @testSetup annotation:

@isTest
private class OpportunityTriggerTests {
    private static void createOpportunities(...) {
        ...
    }
 
    private static void createProducts(...) {
        ...
    }
 
    private static void createPricebookEntries(...) {
        ...
    }
 
    private static void createOpportunityLineItems(...) {
        ...
    }
 
    @testSetup
    private static void setup() {
        createOpportunities(...);
        createProducts(...);
        createPricebookEntries(...);
        createOpportunityLineItems(...);
    }
 
    @isTest
    private static void testMethod1() {
        // Select the Opportunities you want to manipulate out of your test data
        List<Opportunity> opportunitiesToUpdate = [SELECT Id, ... FROM Opportunity WHERE ...];
 
        for(Opportunity o : opportunitiesToUpdate) {
            // Make changes to each Opportunity
        }
 
        update opportunitiesToUpdate;
 
        List<OpportunityLineItem> updatedLineItems = [SELECT Id, ... FROM OpportunityLineItem WHERE ...];
        // Perform your assertions
    }
 
    @isTest
    private static void testMethod2() {
        // Select some data and do some testing
        ...
    }
 
    @isTest
    private static void testMethod3() {
        // Select some data and do some testing
        ...
    }
}
As you can see, at the start of each test method the code dives straight into doing things that are relevant to the situation that you are testing rather than having to skip through multiple lines of boilerplate setup code.

It also helps to make your tests easier to understand as they are all beginning from a common pre-populated database without having to resort to the evil that is @isTest(SeeAllData = true).

A brave new world

By using the @testSetup annotation you can remove a lot of the repeated code that inevitably ends up at the start of your test methods and concentrate on actually writing your tests.

Another advantage is that you know that at the beginning of each one the database will be in a consistent state.

This could also be the case in the first example, but there is always a risk that you could change the parameters passed into your setup methods in one test but not in the others and end up with hard to track down bugs due to inconsistencies. Using the @testSetup annotation helps to mitigate this risk.

Finally, @testSetup is more efficient and can potentially reduce test execution times due to the way the rollback between test methods is performed by the system.

Rather than performing a full rollback to a clean database between each test method and then recreating everything from scratch, only the changes made by the test method are rolled back so that you are left with your clean @testSetup data.

@testSetup isn’t the answer to everything though, for test classes where you don’t share a lot of common data between tests it’s usefulness is limited. For example when testing a simple page extension that doesn’t interact with large volumes of existing data.

For anything involving triggers or bulk actions though @testSetup should be one of the first things you think about when coming to write your tests.

Overall, the addition of the @testSetup annotation to Apex is welcome, and if used sensibly it will make your tests easier to read, make them more robust and save you time.