Thursday, February 5, 2015

Spring '15 - New Feature Added - @testSetup

One of the very interesting feature added in Spring '15 is @testSetup. In this post I will try to explain with example more about the feature. 
Below is what you will get from release note about @testSetup.

Now let's check with example -

Below is the class/methods for which we need to write test methods - 
public class AccountHelper {
    public List<Account> getAllAccounts(){
        List<Account> allAccounts = [Select Id from Account];
        return allAccounts;
    }
    
    public Integer getEmployeeCountFrom(String accountNumber){
        Account fetchedAccount = [Select Id, NumberOfEmployees from Account where AccountNumber = :accountNumber LIMIT 1];
        return fetchedAccount.NumberOfEmployees;
    }
    
    public List<Case> getAllCases(Id accountId){
        return [select ID from Case where AccountId = :accountId];
    }
    
    public void updateEmployeeCountFor(String accountNumber, Integer newEmployeeCount){
        Account fetchedAccount = [Select Id, NumberOfEmployees from Account where AccountNumber = :accountNumber LIMIT 1];
        fetchedAccount.NumberOfEmployees = newEmployeeCount;
        update fetchedAccount;
    }
}

Previously we used to write test classes like below -
@isTest
public class AccountHelperTest {
    static testMethod void testGetAllAccounts(){
        List<Account> accounts = new List<Account>();
        for(Integer i=0;i < 100;i++){
            accounts.add(new Account(Name = 'Universal Container'));
        }
        insert accounts;
        
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        List<Account> allAccounts = accountHelper.getAllAccounts();
        Test.stopTest();
        
        System.assertEquals(100, allAccounts.size());
    }
    
    static testMethod void testGetEmployeeCountFrom(){
        Account anAccount = new Account();
        anAccount.Name = 'Universal Container';
        anAccount.AccountNumber = 'TEST';
        anAccount.NumberOfEmployees = 100;
        
        insert anAccount;
        
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        Integer numberOfEmployees = accountHelper.getEmployeeCountFrom('TEST');
        Test.stopTest();
        
        System.assertEquals(100, numberOfEmployees);
    }
    
    static testMethod void testGetAllCases(){
        //Create the account first
        Account anAccount = new Account();
        anAccount.Name = 'Universal Container';
        insert anAccount;
        
        //Create the cases now
        List<Case> cases = new List<Case>();
        for(Integer i=0;i < 10;i++){
            cases.add(new Case(
                Status = 'New',
                Priority = 'Medium',
                AccountId = anAccount.Id,
                Origin = 'Phone'
            ));
        }
        insert cases;
        
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        List<Case> allCases = accountHelper.getAllCases(anAccount.id);
        Test.stopTest();
        
        System.assertEquals(10, allCases.size());
    }
}

The disadvantages with this approach is that for every test method we need to write block of codes to setup data. Why not create test data for all the test methods in a test class once and use them everywhere. More organised, right? Exactly that is what we can do now with methods annotated with @testSetup. The method annotated with @testSetup will be responsible for setting up test data at the very beginning before executing the first test method. When all the test methods are executed successfully, the test data will be roll backed automatically. Another advantage with this approach is that if any test method modifies the test data, the modification will be roll backed after executing that particular test method. So the next test method will get the original test data being setup at the beginning.

Let's see how we can write test methods using new approach -
@isTest
public class ApexHelperTestAdvanced {
    @testSetup static void setup() {
        // Create common test accounts
        List<Account> accounts = new List<Account>();
        for(Integer i=0;i < 100;i++){
            accounts.add(new Account(Name = 'Universal Container'));
        }
        
        //Add few account details to one Account
        accounts[0].AccountNumber = 'TEST';
        accounts[0].NumberOfEmployees = 100;
        
        //Insert Account
        insert accounts;
        
        //Add Case Details
        List<Case> cases = new List<Case>();
        for(Integer i=0;i < 10;i++){
            cases.add(new Case(
                Status = 'New',
                Priority = 'Medium',
                AccountId = accounts[0].Id,
                Origin = 'Phone'
            ));
        }
        
        //Insert cases
        insert cases;        
    }
    
    static testMethod void testGetAllAccounts(){
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        List<Account> allAccounts = accountHelper.getAllAccounts();
        Test.stopTest();
        
        System.assertEquals(100, allAccounts.size());
    }
    
    static testMethod void testGetEmployeeCountFrom(){
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        Integer numberOfEmployees = accountHelper.getEmployeeCountFrom('TEST');
        Test.stopTest();
        
        System.assertEquals(100, numberOfEmployees);
    }
    
    static testMethod void testGetAllCases(){
        //Fetch the account ID
        Account fetchedAccount = [Select Id from Account where AccountNumber = 'Test'];
        
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        List<Case> allCases = accountHelper.getAllCases(fetchedAccount.id);
        Test.stopTest();
        
        System.assertEquals(10, allCases.size());
    }
    
    static testMethod void testUpdateEmployeeCountFor(){
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        accountHelper.updateEmployeeCountFor('TEST',200);
        Integer numberOfEmployees = accountHelper.getEmployeeCountFrom('TEST');
        Test.stopTest();
        
        System.assertEquals(200, numberOfEmployees);
    }
    
    static testMethod void testRollBackFunctionality(){
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        Integer numberOfEmployees = accountHelper.getEmployeeCountFrom('TEST');
        Test.stopTest();
        
        System.assertEquals(100, numberOfEmployees);
    }
}

Here as you can see we have a method named setup annotated with @testSetup and this method will setup all the test data. Now from the test methods we are not calling any method or doing something to setup data. It is being setup already by the method "setup" at the beginning. Great, Awesome. So we are moving towards TDD (Test Driven Development) approach

Another point to notice here is that the testMethod name testUpdateEmployeeCountFor is basically modifying the test data by making the number of employees to 200. Now in the next test method i.e. testRollBackFunctionality, we are checking whether the number of employees is again 100 (original values setup earlier). It indicates that any modification in test data by any test method will be rolled back at the end of the execution of that test method. 

Below are few points which we should keep in mind as - 

Try this new feature in your own Spring '15 Pre-release Org! 
Please provide your feedback. Thanks.

0 comments:

Post a Comment