Skip to content

Latest commit

 

History

History
448 lines (327 loc) · 20.4 KB

apex.md

File metadata and controls

448 lines (327 loc) · 20.4 KB

Apex Programming Language

  • Apex is a strongly typed, object-oriented programming language.

  • It allows developers to execute flow and transaction control statements on the Salesforce Platform server.

  • Apex code can only be written in a sandbox environment or a Developer org, not in production.

  • Apex code can be deployed to a production org from a sandbox.

Trailhead | Build Apex Coding Skills

Trailhead | Apex Triggers

  • Apex triggers enable you to perform custom actions before or after events to records in Salesforce, such as insertions, updates, or deletions.

  • Just like database triggers, Apex provides trigger support for managing records.

  • Triggers can be defined for top-level standard objects, such as Account or Contact, custom objects, and some standard child objects.

  • Triggers are active by default when created. Salesforce automatically fires active triggers when the specified database events occur.

  • Typically, you use triggers to perform operations based on specific conditions, to modify related records or restrict certain operations from happening.

Trigger Syntax

trigger TriggerName on ObjectName (trigger_events) {
   code_block
}

trigger HelloWorldTrigger on Account (before insert) {
	System.debug('Hello World!');
}
  • A trigger definition starts with the trigger keyword.

  • It is then followed by the name of the trigger, the Salesforce object that the trigger is associated with, and the conditions under which it fires.

To execute a trigger before or after insert, update, delete, and undelete operations, specify multiple trigger events in a comma-separated list. The events you can specify are:

  • before insert
  • before update
  • before delete
  • after insert
  • after update
  • after delete
  • after undelete

Steps to Create Trigger From Developer Console

  • Setup -> Developer Console -> File | New | Apex Trigger.
  • Enter HelloWorldTrigger for the trigger name, and then select Account for the sObject. Click Submit.

Steps to Create Trigger From VSCode

Types of Triggers

  • Before triggers are used to update or validate record values before they’re saved to the database.

  • After triggers are used to access field values that are set by the system (such as a record's Id or LastModifiedDate field), and to affect changes in other records. The records that fire the after trigger are read-only.

Using Context Variables

  • To access the records that caused the trigger to fire, use context variables.

  • Trigger.New contains all the records that were inserted in insert or update triggers.

  • Trigger.Old provides the old version of sObjects before they were updated in update triggers, or a list of deleted sObjects in delete triggers.

In below example, it iterates over each account in a for loop and updates the Description field for each.

trigger HelloWorldTrigger on Account (before insert) {
    for(Account a : Trigger.New) {
        a.Description = 'New description';
    }   
}

The system saves the records that fired the before trigger after the trigger finishes execution.

You can modify the records in the trigger without explicitly calling a DML insert or update operation.

If you perform DML statements on those records, you get an error.

Another Example showing more than one events.

trigger ContextExampleTrigger on Account (before insert, after insert, after delete) {
}

Trigger Context Variables

Trailhead | Asynchronous Apex

Trailhead | Find and Fix Bugs with Apex Replay Debugger

  • You can write and execute test cases for your classes and triggers with Apex testing framework on lightning platform.

  • Apex unit tests and code coverage of minimum 75% are requirements for deploying and distributing Apex.

Before each major service upgrade, Salesforce runs all Apex tests on your behalf through a process called Apex Hammer. The Hammer process runs in the current version and next release and compares the test results. This process ensures that the behavior in your custom code hasn’t been altered as a result of service upgrades. The Hammer process picks orgs selectively and doesn’t run in all orgs. Issues found are triaged based on certain criteria. Salesforce strives to fix all issues found before each new release.

Code Coverage Requirement for Deployment

What's Code Coverage

This metric shows the number of code lines executed during test case execution, Nothing else!

average = (number of lines executed by test cases)/(total numer of line) * 100%
  • Blue lines covered by test cases
  • Red lines are not covered by test cases.

test-coverage1

Deployment Requirement

Before you can deploy your code or package it for the Salesforce AppExchange, the following must be true.

  • Unit tests must cover at least 75% of your Apex code, and all of those tests must complete successfully.

  • Every trigger must have some test coverage.

  • All classes and triggers must compile successfully.

  • Single Actions
    • Test to verify that a single record produces the correct, expected result.
  • Bulk Actions
    • Any Apex code, whether a trigger, a class or an extension, may be invoked for 1 to 200 records.
    • You must test not only the single record case, but the bulk cases as well.
  • Positive Behavior
    • Test to verify that the expected behavior occurs through every expected permutation, that is, that the user filled out everything correctly and did not go past the limits.
  • Negative Behavior
    • There are likely limits to your applications, such as not being able to add a future date, not being able to specify a negative amount, and so on.
    • You must test for the negative case and verify that the error messages are correctly produced as well as for the positive, within the limits cases.
  • Restricted User
    • Test whether a user with restricted access to the sObjects used in your code sees the expected behavior.
    • That is, whether they can run the code or receive error messages.

If you want to test private/protected members (methods, variables, classes), you have 2 options

  1. Make a public method which uses private members.
  2. Annotate private/protected members of a class with @TestVisible so that you can access them from your test class and use those members.

Example Test Syntax

  • Test classes can be either private or public.
  • If you’re using a test class for unit testing only, declare it as private.
  • Public test classes are typically used for test data factory classes.
  • Test methods must be defined in test classes, which are classes annotated with isTest.
  • The visibility of a test method doesn’t matter, so declaring a test method as public or private doesn’t make a difference as the testing framework is always able to access test methods.
  • For this reason, the access modifiers are omitted in the syntax.
  • Declare method as static.
@isTest
private class MyTestClass {
    @isTest static void myTest() {
        System.assertEquals('Expected Value',
                            'Actual Value', 
                            'Failed Message');
    }
}
  • The verifications are done by calling the System.assertEquals() method, which takes two parameters: the first is the expected value, and the second is the actual value.
  • There is another version of this method that takes a third parameter—a string that describes the comparison being done.
  • This optional string is logged if the assertion fails.

Steps to Apex Testing From Developer Console

  1. In the Developer Console, click File -> New -> Apex Class, and enter the class name e.g; TestTemperatureConverter for the class name, and then click OK.
  2. Annotate class and it's methods with @isTest
  3. In the Developer Console, click Test -> New Run
  4. Under Test Classes, select your class, e.g; TestTemperatureConverter.
  5. To add all the test methods in the TestTemperatureConverter class to the test run, click Add Selected.
  6. Click Run.
  7. In the Tests tab, you see the status of your tests as they’re running. Expand the test run, and expand again until you see the list of individual tests that were run. They all have green checkmarks.

test-result

After you run tests, code coverage is automatically generated for the Apex classes and triggers in the org. You can check the code coverage percentage in the Tests tab of the Developer Console.

test-coverage

While one test method would have resulted in full coverage of the class in question, it’s still important to test for different inputs to ensure the quality of your code.

Create and Execute a Test Suite

  • The TestDataFactory class is a special type of class—it is a public class that is annotated with isTest and can be accessed only from a running test.
  • Test utility classes contain methods that can be called by test methods to perform useful tasks, such as setting up test data.
  • Test utility classes are excluded from the org’s code size limit.

Testing Call Outs (REST/HTTP and SOAP)

  • By default, test methods don’t support SOAP and REST callouts, and tests that perform web service callouts fail.

SOAP Call Outs Testing

  • For SOAP, Apex provides the built-in WebServiceMock interface and the Test.setMock method.

  • Use WebServiceMock and Test.setMock to receive fake responses in a test method.

  • WSDL2Apex auto-generated class call WebServiceCallout.invoke, which performs the callout to the external service.

  • When testing these methods, you can instruct the Apex runtime to generate a fake response whenever WebServiceCallout.invoke is called.

  • To do so, implement the WebServiceMock interface and specify a fake response for the Apex runtime to send.

Step # 01
  • First, implement the WebServiceMock interface and specify the fake response in the doInvoke method.
  • The class implementing the WebServiceMock interface can be either global or public.
  • You can annotate this class with @isTest because it is used only in a test context. In this way, you can exclude it from your org’s code size limit of 6 MB.
@isTest
global class CalculatorCalloutMock implements WebServiceMock {
   global void doInvoke(
           Object stub,
           Object request,
           Map<String, Object> response,
           String endpoint,
           String soapAction,
           String requestName,
           String responseNS,
           String responseName,
           String responseType) {
        // start - specify the response you want to send
        calculatorServices.doAddResponse response_x = 
            new calculatorServices.doAddResponse();
        response_x.return_x = 3.0;
        // end
        response.put('response_x', response_x); 
   }
}
Step # 02
  • Call the service.
public class AwesomeCalculator {
    public static Double add(Double x, Double y) {
        calculatorServices.CalculatorImplPort calculator = 
            new calculatorServices.CalculatorImplPort();
        return calculator.doAdd(x,y);
    }
}
Step # 03
  • Now that you have specified the values of the fake response, instruct the Apex runtime to send this fake response by calling Test.setMock in your test method.

  • For the first argument, pass WebServiceMock.class, and for the second argument, pass a new instance of your interface implementation of WebServiceMock.

  • After this point, if a web service callout is invoked in test context, the callout is not made. You receive the mock response specified in your doInvoke method implementation.

@isTest
private class AwesomeCalculatorTest {
    @isTest static void testCallout() {              
        // This causes a fake response to be generated
        Test.setMock(WebServiceMock.class, new CalculatorCalloutMock());
        // Call the method that invokes a callout
        Double x = 1.0;
        Double y = 2.0;
        Double result = AwesomeCalculator.add(x, y);
        // Verify that a fake result is returned
        System.assertEquals(3.0, result); 
    }
}

REST Call Outs Testing

  • For REST, Apex provides the built-in HttpCalloutMock interface to specify the response sent in the respond method, which the Apex runtime calls to send a response for a callout.
Step # 01
  • First, implement the HttpCalloutMock interface and specify the fake response in the response method.

  • The class implementing the HttpCalloutMock interface can be either global or public.

  • You can annotate this class with @isTest because it is used only in a test context. In this way, you can exclude it from your org’s code size limit of 6 MB.

@isTest
global class AnimalsHttpCalloutMock implements HttpCalloutMock {
    // Implement this interface method
    global HTTPResponse respond(HTTPRequest request) {
        // Create a fake response
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setBody('{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}');
        response.setStatusCode(200);
        return response; 
    }
}
Step # 02
  • Call the method which have REST callout and provide the mock response before executing the method
@isTest 
static void testPostCallout() {
    // Set mock callout class 
    Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock()); 
    // This causes a fake response to be sent
    // from the class that implements HttpCalloutMock. 
    HttpResponse response = AnimalsCallouts.makePostCallout();
    // Verify that the response received contains fake values
    String contentType = response.getHeader('Content-Type');
    System.assert(contentType == 'application/json');
    String actualValue = response.getBody();
    System.debug(response.getBody());
    String expectedValue = '{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}';
    System.assertEquals(expectedValue, actualValue);
    System.assertEquals(200, response.getStatusCode());
}
  • It's possible that you have to make multiple callouts in one test method. To provide mock response, following code will do the trick.

  • And even if you don't have multiple requests in one test method, below code is good approach to provide response in one file for multiple use cases.

private class Mock implements HttpCalloutMock {
        public HTTPResponse respond(HTTPRequest req) {
            if (req.getEndpoint().endsWith('abc')) {
                HTTPResponse res = new HTTPResponse();
                res.setBody('{}');
                res.setStatusCode(200);
                return res;
            } else if (req.getEndpoint().endsWith('xyz')) {
                ...
            } else {
                System.assert(false, 'unexpected endpoint ' + req.getEndpoint());
                return null;
            }
        }
    }

And you can use it like any other mock.

@IsTest
    static void abc() {
        Test.setMock(HttpCalloutMock.class, new Mock());
        ...
    }

    @IsTest
    static void xyz() {
        Test.setMock(HttpCalloutMock.class, new Mock());
        ...
    }

Reference