Thursday, September 14, 2017

Ten Commandments for Salesforce Test

1. Thou shalt not use "Test.isRunning" in actual code
  • Use Mock e.g. Test.setMock(HttpCalloutMock.class, new MyMockHttpService())
  • There might be an extream case, but it is always best practice to keep actual code clean without Test.isRunning


2. Thou shalt use Test.start and stop
  • It resets governer limit!
  • In some cases it is necessary, e.g. if you require to do some DML to setup data and actual test code has callout
  • All asynchronous code gets executed right away - and we never know which code will have asynchronous code now or in future


3. Thou shalt use Test runAs whenever possible
        It makes sure code is running fine as expected profile.

 Profile p = [SELECT Id FROM Profile WHERE Name='Standard User'];   
      User u = new User(Alias = 'standt', Email='standarduser@testorg.com', EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US', LocaleSidKey='en_US', ProfileId = p.Id, TimeZoneSidKey='America/Los_Angeles', UserName='standarduser@testorg.com');  
      System.runAs(u) {  
           System.debug('Current User: ' + UserInfo.getUserName());  
           System.debug('Current Profile: ' + UserInfo.getProfileId());   
      }       


4. Thou shalt use System.assert - after Test.stopTest, and should have meaningful message

  • Must use System.assert
  • It should always be after Test.stopTest, as asynchronous code gets executed on Test.stopTest line
  • Have a meaningful error message : System.assertEquals( A, B, "Message");


5. Thou shalt use @testSetup
  • Only one method can have @testSetup
  • It is called in separate transaction, so don't try to set variable, just use for data prepeartion


6. Thou shalt use private for class
  • class should be marked @isTest and private 


7. Thou shalt use private for method
  • Method should be marked @isTest and private


8. Thou shalt use Test Data Factory
  • Instead of creating test data in class, it should be seperate routine - as chances are same data would be created again.
  • We can use  Test.loadData(Account.sObjectType, 'myResource')  or create based on parameters, but it is good to externalize


9. Thou shalt never use @seeAllData
  • obviously!


10. Test Behavior over Coverage



Based on above, here is my template I use :

My Sample Class:

 /**  
  * Created by chshah on 9/14/2017.  
  */  
 public with sharing class My {  
   public static List<Contact> changeContactName(List<Contact> contacts) {  
     for(Contact c : contacts ) {  
       c.firstName = c.firstName.toUpperCase();  
     }  
     update contacts;  
     return contacts;  
   }  
 }  


Test Class:

 /**  
  * Created by chshah on 9/14/2017.  
  */  
 @isTest  
 private class MyTest {  
   @testSetup  
   private static void testSetup() {  
   }  
   @isTest  
   private static void testChangeContactName() {  
     Profile p = [SELECT Id FROM Profile WHERE Name='Standard User'];  
     User u = new User(Alias = 'standt', Email='standarduser@testorg.com', EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US', LocaleSidKey='en_US', ProfileId = p.Id, TimeZoneSidKey='America/Los_Angeles', UserName='standarduser@testorg.com.testorg');  
     System.runAs(u) {  
       Contact c = MyTestFactory.createContact('Chintan','Shah');  
       Test.startTest();  
       List<Contact> contacts = My.changeContactName( new List<Contact> {c} );  
       Test.stopTest();  
       for(Contact con : contacts ) {  
         System.assertEquals(con.firstName, con.firstName.toUpperCase(), ' firstName must be in upper case ' + con.firstName );  
       }  
     }  
   }  
 }  


Data Factory:


 /**  
  * Created by chshah on 9/14/2017.  
  */  
 @isTest  
 public class MyTestFactory {  
   @TestVisible  
   private static Contact createContact(String firstName, String lastName) {  
     Contact c = new Contact(firstName = firstName, lastName = lastName );  
     insert c;  
     return c;  
   }  
 }  


No comments: