anusha(salesforce developer)

Best Practices of Test Classes in Apex

What are some Best Practices while writing Test Classes ?
Well, It differs from person to person, as every programmer has its own style of writing code. However I will list few of mine.
  • Very important and first, “Test Coverage Target Should not be limited to 75%”. It is not about coverage, It is about testing complete functionality. It will be always better if your code fails during test execution, It will be less devastating than failing functionality after product release.
  • If possible Don’t use seeAllData=true, Create your Own Test Data.
  • Always use @testSetup method dedicated to create test records for that class. This is newly added annotation and very powerful. Any record created in this method will be available to all test methods of that class. Using @testSetup method will improve test execution performance by creating test records only once for that class. If you are not using this, means test record is created in each TestMethod which will be very slow. Read this Salesforce documentation for more information.
  • Create Different Class which will create Dummy Data for testing, and use it everywhere (You have to be very careful, as sometimes it may slow down test class execution by creating unnecessary data which does not require by every test methods. So few developer prefer test data creation per Test class)
  • If your Object’s Schema is not changing frequently, you can create CSV file of records and load in static resource. This file will act as Test data for your Test Classes.
  • Use As much as Assertions like System.AssertEquals or System.AssertNotEquals
  • Use Test.startTest() to reset Governor limits in Test methods
  • If you are doing any Asynchronous operation in code, then don’t forget to call Test.stopTest() to make sure that operation is completed.
  • Use System.runAs() method to enforce OWD and Profile related testings. This is very important from Security point of View.
  • Always try to pass null values in every methods. This is the area where most of program fails, unknowingly.
  • Always test Batch Capabilities of your code by passing 20 to 100 records.
  • Use Test.isRunningTest() in your code to identify that context of class is Test or not. You can use this condition with OR (||) to allow test classes to enter inside code bock. It is very handy while testing for webservices, we can generate fake response easily.
  • @TestVisible annotation can be used to access private members and methods inside Test Class. Now we don’t need to compromise with access specifiers for sake of code coverage.
  • End your test class with “_Test”. So that in Apex Class list view, Main class and Test class will come together, resulting easy navigation and time saver.

Use of SmartFactory to auto generate test data
This is very common pattern where we create test data in some utility method ans reuse it across test methods. However change in requirement is inevitable and many validation rules and mandatory fields gets introduced in application and test methods starts breaking. We can resolve this issue by usingunmanaged package available on github which creates Test data and hierarchy automatically using dynamic Apex and make sure all required fields are populated.
Just use SmartFactory in your tests to create objects:
1Account account = (Account)SmartFactory.createSObject('Account');
To cascade and create objects for lookup and master-detail relationships:
1Contact contact = (Contact)SmartFactory.createSObject('Contact'true);
The same syntax is used for custom objects:
1Custom_Object__c customObject = (Custom_Object__c)SmartFactory.createSObject('Custom_Object__c');
How to write Test method of Controller Extension for StandardController ?
Example :
1//Lets Assume we are writing Controller Extension for Account
2Account acct = [SELECT ID FROM Account LIMIT 1];
3 
4//Start Test Context, It will reset all Governor limits
5Test.startTest();
6 
7//Inform Test Class to set current page as your Page where Extension is used
8Test.setCurrentPage(Page.YOUR_PAGE);
9 
10//Instantiate object of "ApexPages.StandardController" by passing object
11ApexPages.StandardController stdController = new ApexPages.StandardController(acct);
12 
13//Now, create Object of your Controller extension by passing object of standardController
14YOUR_Extension ext = new YOUR_Extension(stdController);
15 
16//Here you can test all public methods defined on Extension "ext"
17//..... your code
18 
19//Finish Test
20Test.stopTest();

How to write Test method of Controller Extension for StandardSetController ?
In Same way, with few modification :
1//Lets Assume we are writing Controller extension to use on List View of Account
2List <Account> acctList = [SELECT ID FROM Account];
3 
4//Start Test Context, It will reset all Governor limits
5Test.startTest();
6 
7//Inform Test Class to set current page as your Page where Extension is used
8Test.setCurrentPage(Page.YOUR_PAGE);
9 
10//Instantiate object of "ApexPages.StandardSetController"by passing array of records
11ApexPages.StandardSetController stdSetController = new ApexPages.StandardSetController(acctList);
12 
13//Now, create Object of your Controller extension by passing object of standardSetController
14YOUR_Extension ext = new YOUR_Extension(stdSetController);
15 
16//Here you can test all public methods defined on Extension "ext"
17//..... your code
18 
19//Finish Test
20Test.stopTest();

Why getSelected() method of StandrdSetController is not returning anything?
You might be in situation that getSelected() method is not returning anything in your test method  inspite of proper coding. In Actual scenario we select records in ListView and after clicking on custom button, StandardSetController’s getSelected() method returns selected record in Controller Extension. So, to Select records programmatically  in Test method we have to use method “setSelected()” as shown in below code.
1//Lets Assume we are writing Controller extension to use on List View of Account
2List <acctList> = [SELECT ID FROM Account];
3 
4//Start Test Context, It will reset all Governor limits
5Test.startTest();
6 
7//Inform Test Class to set current page as your Page where Extension is used
8Test.setCurrentPage(Page.YOUR_PAGE);
9 
10//Instantiate object of "ApexPages.StandardSetController" by passing array of records
11ApexPages.StandardSetController stdSetController = new ApexPages. StandardSetController(acctList);
12 
13//Here, you are selecting records programmatically, which will be available to method "getSelected()"
14stdSetController.setSelected(acctList);
15 
16//Now, create Object of your Controller extension by passing object of standardSetController
17YOUR_Extension ext = new YOUR_Extension(stdSetController);
18 
19//Here you can test all public methods defined on Extension "ext"
20//..... your code
21 
22//Finish Test
23Test.stopTest();

Any sample Template for method or class which generates Dummy Data?
You can create methods something like below to generate TestRecords. It will change on requirement however will give some idea.
1/**
2*   Utility class used for generating Dummy Data for Test Methods
3**/
4public class DummyData
5{
6    /**
7    *   Description : This method will generate List of Account with Dummy Data
8    *
9    *   Parameters :
10    *   @totalRecords : How many Records you want to generate ?
11    *   @withIds : Do you want returned records with generateId? If null then false
12    **/
13    public static List<Account> getAccounts(Integer totalRecords, Boolean withIds)
14    {
15        List<Account> retList = new List<Account>();
16        if(withIds == null)
17            withIds = false;
18 
19        for(Integer i=0;i<totalRecords;i++)
20        {
21            Account a = new Account(Name = constructTestString(20));
22            retList.add(a);
23        }
24        if(withIds)
25            insert retList;
26 
27        return retList;
28    }
29 
30    /**
31    *   This method is used to generate Random String of supplied length
32    */
33    public static String constructTestString(Integer length) {
34        Blob blobKey = crypto.generateAesKey(128);
35        String key = EncodingUtil.convertToHex(blobKey);
36        return key.substring(0,length);
37    }
38}

How to check Error Page Messages in Test Classes to ensure that messages are displayed as expected on Visualforce Page ?
Assume that you have one Controller class which adds error message to Visualforce page.
1public class DemoController
2{
3    //... some code
4 
5    public PageReference somemethod()
6    {
7        //Check for some validation. like User is Authorized or not ?
8        if(!validate())
9        {
10             //Means User is not Authorized for this operation, Add error on Visualforce page
11             Apexpages.addMessage( new ApexPages.Message (ApexPages.Severity.ERROR, 'User is not Authorized to perform this Operation'));
12             return null;
13        }
14        return Page.SomePage;
15    }
16}
Now, lets say you want to check whether error message is added on visualforce page or not in Test Class.
1@isTest
2public class DemoController_Test
3{
4    public static testMethod void someMethod_Test()
5    {
6        Test.StartTest();
7 
8        //Set Context for Current page in Test Method
9        Test.setCurrentPage(Page.yourpage);
10 
11        //... some code Here, which will produce error in Apex:PageMessages tag
12        List<Apexpages.Message> msgs = ApexPages.getMessages();
13 
14        boolean isErrorMessage = false;
15 
16        for(Apexpages.Message msg : msgs){
17            if (msg.getDetail().contains('User is not Authorized to perform this Operation') )
18                isErrorMessage  = true;
19        }
20        //Assert that the Page Message was Properly Displayed
21        system.assert(isErrorMessage );
22 
23    }
24}
If you are looking on how to add “Page error message” in Visualforce through Apex, You can see this article.

How to set Page Parameters for Visualforce page in Test Classes ?
First, we need to set Current page in Test method and use “ApexPages” class. Example of code snippet shown below :
1@isTest
2public class Your_TestClass
3{
4    public static testMethod void yourmethodName()
5    {
6        //... your initialization code here
7 
8        //Set current Page Context here, consider page name "Demo"
9        Test.setCurrentPage(Test.Demo);
10 
11        //Now set Parameters for page "Demo"
12        ApexPages.currentPage( ).getParameters( ).put( 'parameterName' 'param Value');
13 
14        //... Your remaining logic here
15    }
16}

No comments:

Post a Comment