Thursday, 26 January 2017

                       Apex REST Callouts  


HTTP and Callout Basics

REST callouts are based on HTTP. To understand how callouts work, it’s helpful to understand a few things about HTTP. Each callout request is associated with an HTTP method and an endpoint. The HTTP method indicates what type of action is desired.

Apex callouts to an external service


Get Data from a Service

1.Open the Developer Console under Your Name or the quick access menu (Setup gear icon).
2.In the Developer Console, select Debug | Open Execute Anonymous Window.
3.Delete the existing code and insert the following snippet.

Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
request.setMethod('GET');
HttpResponse response = http.send(request);
// If the request is successful, parse the JSON response.
if (response.getStatusCode() == 200) {
    // Deserialize the JSON string into collections of primitive data types.
    Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
    // Cast the values in the 'animals' key as a list
    List<Object> animals = (List<Object>) results.get('animals');
    System.debug('Received the following animals:');
    for (Object animal: animals) {
        System.debug(animal);
    }
}

4.Select Open Log, and then click Execute.
5. After the debug log opens, select Debug Only to view the output of the System.debug statements.
The names of the animals are displayed.


Send Data to a Service

Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json;charset=UTF-8');
// Set the body as a JSON object
request.setBody('{"name":"mighty moose"}');
HttpResponse response = http.send(request);
// Parse the JSON response
if (response.getStatusCode() != 201) {
    System.debug('The status code returned was not expected: ' +
        response.getStatusCode() + ' ' + response.getStatus());
} else {
    System.debug(response.getBody());
}


                                 Test Callouts:

here’s good news and bad news regarding callout testing. The bad news is that Apex test methods don’t support callouts, and tests that perform callouts fail. The good news is that the testing runtime allows you to “mock” the callout. Mock callouts allow you to specify the response to return in the test instead of actually calling the web service. You are essentially telling the runtime, “I know what this web service will return, so instead of calling it during testing, just return this data.” Using mock callouts in your tests helps ensure that you attain adequate code coverage and that no lines of code are skipped due to callouts.
Before you write your tests, let’s create a class that contains the GET and POST request examples we executed anonymously in the “Send Data to a Service” unit. These examples are slightly modified so that the requests are in methods and return values, but they’re essentially the same as the previous examples.

In the Developer Console, select File | New | Apex Class.
For the class name, enter AnimalsCallouts and then click OK.
Replace the autogenerated code with the following class definition
public class AnimalsCallouts {

    public static HttpResponse makeGetCallout() {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        // If the request is successful, parse the JSON response.
        if (response.getStatusCode() == 200) {
            // Deserializes the JSON string into collections of primitive data types.
            Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
            // Cast the values in the 'animals' key as a list
            List<Object> animals = (List<Object>) results.get('animals');
            System.debug('Received the following animals:');
            for (Object animal: animals) {
                System.debug(animal);
            }
        }
        return response;

    }

    public static HttpResponse makePostCallout() {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
        request.setMethod('POST');
        request.setHeader('Content-Type', 'application/json;charset=UTF-8');
        request.setBody('{"name":"mighty moose"}');
        HttpResponse response = http.send(request);
        // Parse the JSON response
        if (response.getStatusCode() != 201) {
            System.debug('The status code returned was not expected: ' +
                response.getStatusCode() + ' ' + response.getStatus());
        } else {
            System.debug(response.getBody());
        }
        return response;
    }      

}


Test a Callout with StaticResourceCalloutMock


To test your callouts, use mock callouts by either implementing an interface or using static resources. In this example, we use static resources and a mock interface later on. The static resource contains the response body to return. Again, when using a mock callout, the request isn’t sent to the endpoint. Instead, the Apex runtime knows to look up the response specified in the static resource and return it instead. The Test.setMock method informs the runtime that mock callouts are used in the test method.

Let’s see mock callouts in action. First, we create a static resource containing a JSON-formatted string to use for the GET request.

1.In the Developer Console, select File | New | Static Resource.
2.For the name, enter GetAnimalResource.
3. For the MIME type, select text/plain, even though we are using JSON.
4. Click Submit.

In the tab that opens for the resource, insert the following content. Make sure that it is all on one line and doesn’t break to the next line.

{"animals": ["pesky porcupine", "hungry hippo", "squeaky squirrel"]}

5. Press CTRL+S to save.
You’ve successfully created your static resource! Now, let’s add a test for our callout that uses this resource.

1.In the Developer Console, select File | New | Apex Class.
2.For the class name, enter AnimalsCalloutsTest and then click OK.
3.Replace the autogenerated code with the following test class definition.



@isTest
private class AnimalsCalloutsTest {

    @isTest static  void testGetCallout() {
        // Create the mock response based on a static resource
        StaticResourceCalloutMock mock = new StaticResourceCalloutMock();
        mock.setStaticResource('GetAnimalResource');
        mock.setStatusCode(200);
        mock.setHeader('Content-Type', 'application/json;charset=UTF-8');
        // Associate the callout with a mock response
        Test.setMock(HttpCalloutMock.class, mock);
        // Call method to test
        HttpResponse result = AnimalsCallouts.makeGetCallout();
        // Verify mock response is not null
        System.assertNotEquals(null,result,
            'The callout returned a null response.');
        // Verify status code
        System.assertEquals(200,result.getStatusCode(),
          'The status code is not 200.');
        // Verify content type
        System.assertEquals('application/json;charset=UTF-8',
          result.getHeader('Content-Type'),
          'The content type value is not expected.');
        // Verify the array contains 3 items  
        Map<String, Object> results = (Map<String, Object>)
            JSON.deserializeUntyped(result.getBody());
        List<Object> animals = (List<Object>) results.get('animals');
        System.assertEquals(3, animals.size(),
          'The array should only contain 3 items.');        
    }

}


4.Press CTRL+S to save.
5.Select Test | Always Run Asynchronously. If you don’t select Always Run Asynchronously, test runs that include only one class run synchronously. You can open logs from the Tests tab only for synchronous test runs.
6.To run the test, select Test | New Run.
7.From the Test Classes list, select AnimalsCalloutsTest.
8.Click Add Selected | Run.


Test a Callout with HttpCalloutMock


To test your POST callout, we provide an implementation of the HttpCalloutMock interface. This interface enables you to specify the response that’s sent in the respond method. Your test class instructs the Apex runtime to send this fake response by calling Test.setMock again. For the first argument, pass HttpCalloutMock.class. For the second argument, pass a new instance of AnimalsHttpCalloutMock, which is your interface implementation of HttpCalloutMock. (We’ll write AnimalsHttpCalloutMock in the example after this one.)

Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock());

Now add the class that implements the HttpCalloutMock interface to intercept the callout. If an HTTP callout is invoked in test context, the callout is not made. Instead, you receive the mock response that you specify in the respond method implementation in AnimalsHttpCalloutMock.

1.In the Developer Console, select File | New | Apex Class.
2.For the class name, enter AnimalsHttpCalloutMock and then click OK.
3. Replace the autogenerated code with the following class definition.


@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;
    }
}


4.Press CTRL+S to save.

In your test class, create the testPostCallout method to set the mock callout, as shown in the next example. The testPostCallout method calls Test.setMock, and then calls the makePostCallout method in the AnimalsCallouts class. It then verifies that the response that’s returned is what you specified in the respond method of the mock implementation.

Modify the test class AnimalsCalloutsTest to add a second test method. Click the class tab, and then add the following method before the closing bracket.

@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(actualValue, expectedValue);
    System.assertEquals(200, response.getStatusCode());
}

5.Press CTRL+S to save.
6.Select Test | New Run.
7.From the Test Classes list, select AnimalsCalloutsTest.
8.Click Add Selected | Run.


Special Note: 

When making a callout from a method, the method waits for the external service to send back the callout response before executing subsequent lines of code. Alternatively, you can place the callout code in an asynchronous method that’s annotated with @future(callout=true) or use Queueable Apex. This way, the callout runs on a separate thread, and the execution of the calling method isn’t blocked.

When making a callout from a trigger, the callout must not block the trigger process while waiting for the response. For the trigger to be able to make a callout, the method containing the callout code must be annotated with @future(callout=true) to run in a separate thread.





No comments:

Post a Comment