Tuesday, June 27, 2017

Test Class in Salesforce

Why do we need to write test classes?
Ans: Salesforce does not allow deployment in production if the test coverage is less than 75%.

Can custom setting be accessed in a test class?
Ans - Custom setting data is not available default in a test class. We have to set seeAllData parameter true while definig a test class as below.
@isTest(seeAlldata=true)

How much code coverage is needed for deployment?
Ans : Each trigger should have minimum of 1%. Class can have 0%. But, the total code coverage of 75%.



Best Practices for Test Classes in salesforce?


  1. 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 testing, It will be less devastating than failing functionality after product release.
  2. If possible Don’t use seeAllData=true, Create your Own Test Data.
  3. 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)
  4. 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.
  5. Use As much as Assertions like System.AssertEquals or System.AssertNotEquals
  6. Use Test.startTest() to reset Governor limits in Test methods
  7. If you are doing any Asynchronous operation in code, then don’t forget to call Test.stopTest() to make sure that operation is completed.
  8. Use System.runAs() method to enforce OWD and Profile related testings. This is very important from Security point of View.
    As apex runs in system mode so the permission and record sharing are not taken into account . So we need to use system.runAs to enforce record sharing .
  9. Always try to pass null values in every methods. This is the area where most of program fails, unknowingly.
  10. Always test Batch Capabilities of your code by passing 20 to 100 records.
  11. 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.
  12. @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.
  13. Maximum number of test classes run per 24 hour of period is  not grater of 500 or 10 multiplication of test classes of your organization.
  14. Accessing static resource test records in test class e,g List<Account> accList=Test.loadData(Account,SobjectType,'ResourceName').
  15. You can't  send email from test method.
  16. System.debug statement are not counted as a part of apex code limit.
  17.  Test method and test classes are not counted as a part of code limit
  18.  We should not focus on the  percentage of code coverage ,we should make sure that every use case should covered including positive, negative,bulk and single record .Single Action -To verify that the the single record produces the correct an expected result .Bulk action -Any apex record trigger ,class or extension must be invoked for 1-200 records .Positive behavior : Test every expected behavior occurs through every expected permutation , i,e user filled out every correctly data and not go past the limit .Negative Testcase :-Not to add future date , Not to specify negative amount.Restricted User :-Test whether a user with restricted access used in your code .

Test classes with @isTest in salesforce?

Use the isTest annotation to define classes and methods that only contain code used for testing your application. The isTest annotation on methods is equivalent to the testMethod keyword.

1.Classes and methods defined as isTest can be either private or public. Classes defined as isTest must be top-level classes.

2.One advantage to creating a separate class for testing is that classes defined with isTest don't count against your organization limit of 3 MB for all Apex code.
3.You can also add the @isTest annotation to individual methods
4.Classes defined as isTest can't be interfaces or enums
5.Methods of a test class can only be called from a running test, that is, a test method or code invoked by a test method, and can't be called by a non-test request.
6.Test methods can’t be used to test Web service callouts. Instead, use mock callouts
7.You can’t send email messages from a test method
?
1
2
3
4
5
6
7
8
9
10
@isTest
                     
private class MyTestClass {
   @isTest static void test1() {
      // Implement test code
   }
   @isTest static void test2() {
      // Implement test code
   }
}
IsTest(SeeAllData=true) Annotation

use the isTest(SeeAllData=true) annotation to grant test classes and individual test methods access to all data in the organization,


If a test class is defined with the isTest(SeeAllData=true) annotation, this annotation applies to all its test methods whether the test methods are defined with the @isTest annotation or the testmethod keyword

The isTest(SeeAllData=true) annotation is used to open up data access when applied at the class or method leve

IsTest(OnInstall=true) Annotation


Use the IsTest(OnInstall=true) annotation to specify which Apex tests are executed during package installation. This annotation is used for tests in managed or unmanaged packages

What is TestVisible annotation in Salesforce?


Hi,
This is the documentation notes about @TestVisible annotation in salesforce. 

Use the TestVisible annotation to allow test methods to access private or protected members of another class outside the test class. These members include methods, member variables, and inner classes. This annotation enables a more permissive access level for running tests only. This annotation doesn’t change the visibility of members if accessed by non-test classes.

With this annotation, you don’t have to change the access modifiers of your methods and member variables to public if you want to access them in a test method. For example, if a private member variable isn’t supposed to be exposed to external classes but it should be accessible by a test method, you can add the TestVisible annotation to the variable definition.

This example shows how to annotate a private class member variable and private method with TestVisible.


public class TestVisibleExample {
    // Private member variable
    @TestVisible private static Integer recordNumber = 1;

    // Private method
    @TestVisible private static void updateRecord(String name) {
        // Do something
    }
}    

This is the test class that uses the previous class. It contains the test method that accesses the annotated member variable and method.

@isTest
private class TestVisibleExampleTest {
    @isTest static void test1() {
        // Access private variable annotated with TestVisible
        Integer i = TestVisibleExample.recordNumber;
        System.assertEquals(1, i);

        // Access private method annotated with TestVisible
        TestVisibleExample.updateRecord('RecordName');
        // Perform some verification
    }


How to writeTest class for batch apex in salesforce?

Let us learn to write a test class that covers a batch apex class. Nothing better than learning from a working example.
Here is a test class for a batch apex class that updates account records that are passed through a select query.
Batch apex clas
global class BatchProcessAccount implements Database.Batchable<sObject>{
 String query;
global Database.querylocator start(Database.BatchableContext BC){
        Query = 'Select id,name,AccountNumber,type from account';
        return Database.getQueryLocator(query);
 }
 global void execute(Database.BatchableContext BC, List<account> scope){
       List<Account> AccountList = new List<Account>();
       for(account acc : scope){
           acc.AccountNumber= '8888';
           AccountList.add(acc);
       }
       update AccountList ;
    }
   global void finish(Database.BatchableContext BC){
    }
}
Test class for above batch apex class
@isTest
private class BatchProcessAccount_Test {
     
static testMethod void BatchProcessAccount_TestMethod (){
     Profile prof = [select id from profile where name='system Administrator'];
     User usr = new User(alias = 'usr', email='us.name@vmail.com',
                emailencodingkey='UTF-8', lastname='lstname',
                timezonesidkey='America/Los_Angeles',
                languagelocalekey='en_US',
                localesidkey='en_US', profileid = prof.Id,
                username='testuser128@testorg.com');
                insert usr;
   Account accRec = new Account(name='testName', Ownerid = usr.id);
   insert accRec ;
   Test.StartTest();
   BatchProcessAccount objBatch = new BatchProcessAccount();
   ID batchprocessid = Database.executeBatch(objBatch);
   Test.StopTest();
  }
}




Just create a instance of the batch apex class: BatchProcessAccount objBatch = new BatchProcessAccount();


and then pass the craeted varaible in executebatch method as below:
   ID batchprocessid = Database.executeBatch(objBatch);

 --------------------------------------------------------------------------------------------

How to cover code coverage Formula field in if condition :



trigger Emailtemplatestudenttrigger on Student__c (before insert ,before update) {

     

    for(student__c s : trigger.new ){

        
        string str = '';
                str='<html>';
                str=str+'<body>';
                str=str+'Hello '+s.Name+',<br/>'; 
                str=str+'<p>  Please chech your total marks below.<br/> </p>';
                str=str+'<p> Your Maths marks are '+s.maths__c+'  <br/> </p>';
                str=str+'<p> Your Telugu marks are : '+s.Telugu__c+'  <br/> </p>';
                str=str+'<p> Your English marks are : '+s.English__c+'  <br/> </p>';
                str=str+'<p> Your Maths marks are :'+s.maths__c+'  <br/> </p>';
                str=str+'<p> Your Maths1 marks are :'+s.Maths1__c+'  <br/> </p>';
                str=str+'<p> Your Physics marks are : '+s.Phisics__c+'  <br/> </p>';
                str=str+'<p> Your Chemistry marks are : '+s.Chemistry__c +'  <br/> </p>';
                str=str+'<p> Your Total marks are '+s.Average__c +'  <br/> </p>';
                str=str+'<p> Thank you  <br/> </p>';
                str=str+'<p> Admin  <br/> </p>';
                str=str+'</body>';
                str=str+'</html>';
        
       // if(s.email__c != null || s.Email__c !=''){
        if(S.Formula_status__c =='Proposal Submitted'|| S.Formula_status__c=='Final From Submitted' ||Test.isRunningTest()){
             Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

       // EmailTemplate et = [Select Id from EmailTemplate where Name = 'Student Email Template'];
        /// mail.setTargetObjectId(userinfo.getUserId());
        // mail.setTemplateId(et.id);
        // mail.setTreatTargetObjectAsRecipient(false);
         mail.setSubject('Student Marks Details');
         mail.setHtmlBody(str);
         mail.setToAddresses(new List<String>{s.email__c});
        // mail.setSaveAsActivity(false); 
         Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail});
         }
      }
}
----------------------------
Test Class: Test.isRunningTest()

@istest
public class EmailtemplatestudenttriggerTest {
    @istest static void testmethod1(){
           Product2 prod = new Product2(Name = 'Laptop X200',Family = 'Hardware');
        insert prod;

    student__c s = new student__c();
    s.name = 'venkat';
    s.maths__c = 123;
    s.maths1__c = 234;
        s.Telugu__c = 234;
        s.English__c = 231;
        s.Chemistry__c = 564;
        s.Phisics__c = 124;
        s.Email__c = 'test@gmail.com';
        s.Product__c = prod.id;
      //  s.Formula_status__c = 'Proposal Submitted';
        insert s;
    }
}


Set Up Test Data for an Entire Test Class & @TestSetup method in Apex.


In old school way, if we wanted to create records for test methods, helper or utility classes were favourite option for developers.

However this approach has few disadvantages
  • Multiple Test classes could depend on these utility methods
  • It creates tight coupling between classes
  • Deployment could be slow if unnecessary test record being created for test methods
  • If class to create test record is not marked with @isTest then it will count against allowed apex code in instance
  • Test classes needs to explicitly call method to create test records

Test Setup (@testSetup) Methods in Test Class:

@testSetup to create test records once in a method  and use in every test method in the test class .

Apex has introduced new method in Test class known as “Test Setup”. Following are some highlights and considerations of using Test Setup method in Test classes :

  • It needs to be marked with @testSetup annotation in Test class
  • One Test class can have multiple @testSetup methods
  • These test setup methods are implicitly invoked before each test methods
  • These methods can be used to create test records specific to Test class
  • It helps to keep Test classes isolated and independent of helper or utility classes
  • It can’t be used if Test class is marked with @isTest(SeeAllData=true)
  • If error occurs in setup method then entire test class fails
  • If non-test method is called from setup method then no code coverage is calculated for non-test method
  • If multiple set up method is written in Test class then sequence of execution of those methods are not guaranteed

Below class shows sample code of Test method in action. It creates 200 lead records in test setup method (@testSetup) and checks whether 200 records are created or not in test method.

@iSTest
public class LeadProcessorTest { 
 //below test setup method will be invoked 
 // implicitly before every test methods 
 @testsetup
    static void createLead(){ 
 List<Lead> lstLead = new List<Lead>(); 
 for(Integer i = 0 ; i<200 ; i++) {
 lstLead.add(new Lead(lastName = 'testLName'+i , Company = 'Salesforce'));
 } 
        insert lstLead ;
 }
    
   //Check whether records created in test method 
 //is accessible or not 
 public static testMethod void test(){ 
 System.assertEquals(200, [SELECT COUNT() FROM Lead Where company = 'Salesforce']);
    } 
}


Salesforce Documentation for Using Test Setup Methods

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_testsetup_using.htm

@istest
public class TestUtilClass {
    public static Account createAccount(String name) {
        return new Account(Name=name);
    }
     public static Account createAccount(Integer identifier, Boolean insertToDatabase) {
        Account testAccount = new Account(Name = 'Test Account' + identifier);
        if (insertToDatabase) {
            insert testAccount;
        }
        return testAccount;
    }
    public static Contact createContact(Integer identifier, Id testAccountId, Boolean insertToDatabase) {
        Contact testContact = new Contact(AccountId = testAccountId,
                                          LastName = 'Test111' + identifier,
                                          FirstName = 'John',
                                          Salutation = 'Mr',
                                          Email = 'test12341234@madeupemail1234test.com',
                                          MailingStreet = 'Mailing Street',
                                         MailingCity = 'Mailing City',
                                         MailingState = 'Kent',
                                         MailingPostalCode = 'Mailing Postal Code',
                                         MailingCountry = 'United Kingdom');
        if (insertToDatabase) {
            insert testContact;
        }
        return testContact;
    }
    public static Case createCase (Id contactID, String branch, String queryType, String queryTypeDetail, String assignQuerytoQueue, boolean routeCaseToTeam, boolean insertToDatabase) {
        
        // Id queryCaseRecordType = [select id from RecordType where DeveloperName='Query' limit 1].id;

        Case c = new Case (contactid = contactID, 
                         //   jl_Branch_master__c = branch,
                         //   jl_Query_type__c = queryType,
                         //   jl_Query_type_detail__c = queryTypeDetail,
                          //  jl_Assign_query_to_queue__c = assignQuerytoQueue,
                         //   jl_Transfer_case__c = routeCaseToTeam,
                        //    recordtypeid = queryCaseRecordType,
                            description = 'test',
                            Status = 'New'
                        //    jl_Action_Taken__c = 'New case created'

                    );
        if (insertToDatabase) {
            insert c;

        }
        return c;
     }
    
    public static User getSystemAdministrator (String firstName) {
        Profile prof = [Select Id, Name From Profile Where Name = 'System Administrator'];
        User usr = new User(FirstName = firstName, LastName = 'Admin', Username=firstName+'test@test.com.test', 
                            Email=firstName+'test.test@test.com.test', CommunityNickname=firstName, 
                            Alias='first' ,TimeZoneSidKey='Europe/London', 
                            LocaleSidKey='en_GB', EmailEncodingKey='ISO-8859-1', 
                            ProfileId=prof.Id, LanguageLocaleKey='en_US', isActive=true
                            
                        );
        return usr;
    }
    
    public static AccountContactRole createACR(id conid,id accid){
    AccountContactRole  Objaccrole = new AccountContactRole(ContactId = conid,accountid = accid,
                                                            role ='Customer Primary Contact' ,
                                                           IsPrimary = true);
                                                          // contact.email ='test@gmail.com') ;
       insert Objaccrole; 
        return Objaccrole;
        }
    
}

Rest API Class and Test Class:

public class Rest_SampleCalloutClass {
    Public static httpResponse getInformJsonTestservice(){
        HttpRequest request = new HttpRequest();
        request.setEndpoint('http://cookie.jsontest.com/');
        request.setMethod('GET');
     
        Http http = new Http();
        HttpResponse response= http.send(request);
        return response;
    }

}
-------------
@istest
Global class Rest_MakeMockHttpResponse implements HttpCalloutMock{
    //implements HttpRespose method
    Global HttpResponse respond(HttpRequest request){
        //Request only a jsontest endpoint with get method
        system.assertEquals('http://cookie.jsontest.com/', request.getEndpoint());
        system.assertEquals('GET', request.getMethod());
     
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setBody('{"cookie_status": "Cookie set with name jsontestdotcom"}');
        response.setStatusCode(200);
        Return response;
    }
}
-------------
@istest
public class Rest_SampleCalloutClassTest {
    public static testmethod void testcallout(){
        //set mock method of test class with 2 parameters
        //the 2 parameter is implemetion class of the mock interface
        Test.setMock(HttpCalloutMock.class,new Rest_MakeMockHttpResponse());
     
        HttpResponse response= Rest_SampleCalloutClass.getInformJsonTestservice();
        String contenttype = response.getHeader('Content-Type');
        system.assert(contenttype == 'application/json' );
        string Actualresultvalue = response.getBody();
        string expectedresultvalue = '{"cookie_status": "Cookie set with name jsontestdotcom"}';
        system.assertEquals(Actualresultvalue,expectedresultvalue);
        system.assertEquals(200, response.getStatusCode());
        system.debug('Actualresultvalue'+Actualresultvalue);
        system.debug('expectedresultvalue'+expectedresultvalue);
    }
}
-----------------------------------------------
Test class for Standardsetcontroller and test.setcurrentpage():

@isTest

public class ListViewDemoTest

{   

     

    @testSetup

    static void createAccount() {

        // Create common test accounts

        List<Account> testAccts = new List<Account>();

        for(Integer i=0;i<20;i++) {

            testAccts.add(new Account(Name = 'TestAcct'+i));

        }

        insert testAccts;       

    }

     

     public static testMethod void getListView(){

        //Lets Assume we are writing Controller extension to use on List View of Account

        List <Account> acctList = [SELECT ID FROM Account];

         

         //Check Account created count by setup()

         System.assertEquals(20,acctList.size());

          

        //Start Test Context, It will reset all Governor limits

        Test.startTest();


        //Inform Test Class to set current page as your Page where Extension is used

        Test.setCurrentPage(Page.ListViewDemo);


        //Instantiate object of "ApexPages.StandardSetController" by passing array of records

        ApexPages.StandardSetController stdSetController = newApexPages.StandardSetController(acctList);


        //Now, create Object of your Controller extension by passing object of standardSetController

        ListViewDemo ext = new ListViewDemo(stdSetController);

         

        SelectOption[] selOptions = ext.getAccountExistingViews();

          

36
        //We should not assert count of list View as no control over creation of list view

37
        //but in my Dev org, I know count is 6

38
        System.assertEquals(6,selOptions.size());

39
          

40
         ext.firstPage();

41
         List<Account> accFirsttPage = ext.getAccounts();

42
         System.assertEquals( 10, accFirsttPage.size() );

43
          

44
         ext.next();

45
         ext.prev();

46
         ext.resetFilter();

47
         ext.lastPage();

48
           

49
        //Finish Test

50
        Test.stopTest();

51
     }


52
}




1 comment: