Tag Archives: Apex

Best Practice for Test Classes in Salesforce

  • To deploy to production at-least 75% code coverage is required, But your focus shouldn’t be on the percentage of code that is covered. Instead, you should make sure that every use case of your application is covered, including positive and negative cases, as well as bulk and single records. This should lead to 75% or more of your code being covered by unit tests.
  • Test class must start with @isTest annotation if class version is more than 25.
  • @isTest annotation with test method  is equivalent to testMethod keyword.
  • Test class and method default access is private ,no need to add access specifier.
  • Classes with @isTest annotation can’t be a interface or enum.
  • Test method code can’t be invoked by non test request.
  • Stating with salesforce API 28.0 test method can not reside inside non test classes.
  • Always use @testSetup method dedicated to create test records for that class. This is newly added annotation and very powerful.
  • If possible Don’t use seeAllData=true, Create your Own Test Data. SeeAllData=true will not work for API 23 version earlier.
  • User, profile, organization, AsyncApexjob, Corntrigger, RecordType, ApexClass, ApexComponent ,ApexPage we can access without (seeAllData=true).
  • @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.
  • Test method and test classes are not counted as a part of code limit.
  • Test method takes no argument, commit no data to database, send no email, flagged with testMethod keyword.
  • 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.
  • System.runAs() will not enforce user permission or field level permission.
  • Use As much as Assertions like System.AssertEquals or System.AssertNotEquals.
  • Always test Batch Capabilities of your code by passing 20 to 100 records.
  • Always try to pass null values in every methods. This is the area where most of program fails, unknowingly.
  • Please use call out mock to test web-service call out .
  • System.debug statement are not counted as a part of apex code limit.
  • We can run unit test by using Salesforce Standard UI,Force.com IDE ,Console ,API.
  • 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.

Testing HTTP Callouts by Implementing the HttpCalloutMock Interface in Salesforce

Callout Class:

public class CalloutClass {
    
    public static HttpResponse getInfoFromExternalService() {
        HttpRequest req = new HttpRequest();
        req.setEndpoint('http://api.salesforce.com/foo/bar');
        req.setMethod('GET');
        Http h = new Http();
        HttpResponse res = h.send(req);
        return res;
    }
}

Mock Http Response Generator Class:

@isTest
global class MockHttpResponseGenerator implements HttpCalloutMock {
    
    global HTTPResponse respond(HTTPRequest req) {
        
        System.assertEquals('http://api.salesforce.com/foo/bar', req.getEndpoint());
        System.assertEquals('GET', req.getMethod());
        
        HttpResponse res = new HttpResponse();
        res.setHeader('Content-Type', 'application/json');
        res.setBody('{"foo":"bar"}');
        res.setStatusCode(200);
        return res;
    }
}

Callout Test Class:

@isTest
private class CalloutClassTest {
     @isTest static void testCallout() {
         
        Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());
        
        HttpResponse res = CalloutClass.getInfoFromExternalService();
        
        String contentType = res.getHeader('Content-Type');
        System.assert(contentType == 'application/json');
        
        String actualValue = res.getBody();
        String expectedValue = '{"foo":"bar"}';
        
        System.assertEquals(actualValue, expectedValue);
        System.assertEquals(200, res.getStatusCode());
    }
}

Create Remote Site Settings Using Apex (Metadata API)

Sample Code:

//Metadata Service
MetadataService.MetadataPort service = new MetadataService.MetadataPort();
ServiceCreation.SessionHeader = new MetadataService.SessionHeader_element();
ServiceCreation.SessionHeader.sessionId = UserInfo.getSessionId();

//Metadata for remote site settings
MetadataService.RemoteSiteSetting remoteSiteSettings = new MetadataService.RemoteSiteSetting();
remoteSiteSettings.fullName = 'Google';
remoteSiteSettings.url = 'https://www.google.com';
remoteSiteSettings.description = 'Remote site settings created from apex';
remoteSiteSettings.isActive = true;
remoteSiteSettings.disableProtocolSecurity = false;
MetadataService.SaveResult[] results = service.createMetadata(new List<MetadataService.Metadata> { remoteSiteSettings });

for(MetadataService.SaveResult sr : results){
    if(result.success){
        //Success
        system.debug('Sucess - ' + result);
    }else{
        //Failed
        system.debug('Failed - '+ result.errors[0].message);
    }
}

Note: You need to import all the metadata classes from Metadata WSDL to get this working.

Limit Class and Limit Methods in Salesforce

The Limits methods return the specific limit for the particular governor, such as the number of calls of a method or the amount of heap size remaining.

Because Apex runs in a multitenant environment, the Apex runtime engine strictly enforces a number of limits to ensure that runaway Apex doesn’t monopolize shared resources.

None of the Limits methods require an argument. The format of the limits methods is as follows:

 Integer queryLimitRows = Limits.getLimitQueryRows();

There are two versions of every method: the first returns the amount of the resource that has been used while the second version contains the word limit and returns the total amount of the resource that is available.

Limits Methods:
The following are methods for Limits. All methods are static.

System.debug('Limits.getAggregateQueries - '+ Limits.getAggregateQueries());
System.debug('Limits.getLimitAggregateQueries - '+ Limits.getLimitAggregateQueries());
System.debug('Limits.getCallouts - '+ Limits.getCallouts());
System.debug('Limits.getLimitCallouts - '+ Limits.getLimitCallouts());
System.debug('Limits.getCpuTime - '+ Limits.getCpuTime());
System.debug('Limits.getLimitCpuTime - '+ Limits.getLimitCpuTime());
System.debug('Limits.getDMLRows - '+ Limits.getDMLRows());
System.debug('Limits.getLimitDMLRows - '+ Limits.getLimitDMLRows());
System.debug('Limits.getDMLStatements - '+ Limits.getDMLStatements());
System.debug('Limits.getLimitDMLStatements - '+ Limits.getLimitDMLStatements());
System.debug('Limits.getEmailInvocations - '+ Limits.getEmailInvocations());
System.debug('Limits.getLimitEmailInvocations - '+ Limits.getLimitEmailInvocations());
System.debug('Limits.getFutureCalls - '+ Limits.getFutureCalls());
System.debug('Limits.getLimitFutureCalls - '+ Limits.getLimitFutureCalls());
System.debug('Limits.getHeapSize - '+ Limits.getHeapSize());
System.debug('Limits.getLimitHeapSize - '+ Limits.getLimitHeapSize());
System.debug('Limits.getMobilePushApexCalls - '+ Limits.getMobilePushApexCalls());
System.debug('Limits.getLimitMobilePushApexCalls - '+ Limits.getLimitMobilePushApexCalls());
System.debug('Limits.getQueries - '+ Limits.getQueries());
System.debug('Limits.getLimitQueries - '+ Limits.getLimitQueries());
System.debug('Limits.getQueryLocatorRows - '+ Limits.getQueryLocatorRows());
System.debug('Limits.getLimitQueryLocatorRows - '+ Limits.getLimitQueryLocatorRows());
System.debug('Limits.getQueryRows - '+ Limits.getQueryRows());
System.debug('Limits.getLimitQueryRows - '+ Limits.getLimitQueryRows());
System.debug('Limits.getQueueableJobs - '+ Limits.getQueueableJobs());
System.debug('Limits.getLimitQueueableJobs - '+ Limits.getLimitQueueableJobs());
System.debug('Limits.getSoslQueries - '+ Limits.getSoslQueries());
System.debug('Limits.getLimitSoslQueries - '+ Limits.getLimitSoslQueries());

Webservice Callout From Apex Trigger

In certain scenarios, we need to make the webservice callout from the apex trigger to call an external webservice. We cannot call external web services synchronously from triggers, because calling a web service synchronously from triggers will hold up the database transaction until the callout completed. This will impact performance for other transactions. In this scenario we can invoke callouts from triggers by encapsulating the callouts in @future methods. Here is an example, how to invoke webservice callouts from trigger.

Apex Class:

public class LeadTriggerHandler {
    
    @future (callout=true)
    public static void sendLeadInfo(string firstName, string lastName, string email) {
        try{
            HttpRequest request = new HttpRequest();
            HttpResponse response = new HttpResponse();
            Http http = new Http();
            
            request.setEndpoint('Your Endpoint URL');
            request.setHeader('Content-Type','application/json'); 
            request.setMethod('POST');
            request.setBody('fname='+EncodingUtil.urlEncode(firstName, 'UTF-8')+'&lname='+EncodingUtil.urlEncode(lastName, 'UTF-8')
                            '&email='+EncodingUtil.urlEncode(email, 'UTF-8'));
            request.setCompressed(true);
            
            response = http.send(request);
            if (response.getStatusCode() == 200) {
                System.debug('Response-' + response);
            }
        }
        catch(System.CalloutException e){
            System.debug('Error-' + e.getMessage());   
        }
    }
}

Apex Trigger:

trigger LeadTrigger on Lead (after insert) {
    
    for (Lead objLead : Trigger.new) {
        //make webservice callout 
        LeadTriggerHandler.sendLeadInfo(objLead.FirstName, objLead.LastName, objLead.Email);
    }
}