Writing Unit Tests for RESTful Apex Methods

A Step-by-Step Guide to Salesforce REST API Integration for Apex Developers (Part II)

Author: Ismail Basser, Salesforce Consultant at Bluewave Technology


Introduction

In a previous post, we examined how a developer might handle data operations between Salesforce and an external system using a custom Apex class and REST API calls. We went through the process of establishing a connection with Salesforce using a Connected App, and reviewed in-detail how RESTful Apex methods handle HTTP GET and POST calls. As a natural continuation, we will now examine how to properly unit test those RESTful Apex methods. Specifically, how we can ‘mock’ the request that gets sent to Salesforce.

Begin with a Test Setup Method

Test Setup Methods are a great way to create testable records once and then access them in subsequent test methods in the same test class. Not only are they time-saving when you need to create prerequisite record data but they can also reduce test execution times especially when you’re working with many records. We will use a simple test setup method as follows:

@testSetup
    static void dataSetup() {
        Account acc = new Account(Name = 'IB Test Acc');
        insert acc;
    }

Now that we have a testable account record we can begin to author the unit tests specific to our RESTful Apex methods.

But wait, how do we ‘mock’ a RestRequest?

When we write unit tests for RESTful Apex methods we won’t be communicating with a live external system. Instead, we’ll use an instance of the RestRequest class to simulate external system requests and we can manipulate the behavior of those requests with the following key attributes.

httpMethod (Required) Returns one of the supported HTTP request methods. (GET, POST, etc…)
requestURI (Required) Returns or sets everything after the host in the HTTP request string.
requestBody (Optional) Returns or sets the body of the request.

Unit Testing GET Methods

Let’s start with a positive unit test for a REST API GET call. By a ‘positive test’ here we don’t mean a method that passes but rather a method where the outcome is as expected and the functionality works as intended. For example, let’s retrieve the account record we created in our test setup method using SOQL. Then we can use the account’s Id to perform a mock REST API GET call. To accomplish this, an instance of a RestRequest is needed where the httpMethod is set to “GET” and the requestURI includes the account Id on the end. The request and the response are added to the RestContext like so…

@isTest //positive unit test for the REST API GET call
    static void testGetPositive() {
        Account acc = [SELECT Id, Name FROM Account LIMIT 1];
        
        RestRequest req = new RestRequest(); 
        RestResponse res = new RestResponse();  
        
        req.requestURI = '/services/apexrest/Account/' + acc.Id;
        req.httpMethod = 'GET';
        RestContext.request = req;
        RestContext.response= res;
        
        Test.startTest();
        SampleREST.doGet();
        Test.stopTest(); 
        
        SampleREST.AccountWrapper responseBody =(SampleREST.AccountWrapper)JSON.deserialize(res.responseBody.toString(), SampleREST.AccountWrapper.class);
        System.assertEquals(acc.Id, responseBody.accid);
        System.assertEquals(acc.Name, responseBody.accName);
        System.assertEquals(200, res.statusCode);

    }

Notice above how this unit test follows a natural and intuitive “given, when, then” pattern where the “given” conditions define the testable parameters, the “when” state is surrounded by .startTest() and .stopTest() methods and “then” there are assertions to confirm the outcome.

Ok, let’s take a crack at a negative unit test for a REST API GET call. Again, by a ‘negative test’ we’re referring to the scenario where an account is not successfully retrieved and this is expected. To achieve this let’s use an account Id that does not exist in Salesforce. You can see in the assertion that the body of the response is “No account found with id 234576dsgfh36”.

@isTest //negative unit test for the REST API GET call
    static void testGetNegative() {
        
        RestRequest req = new RestRequest(); 
        RestResponse res = new RestResponse();  
        
        req.requestURI = '/services/apexrest/Account/234576dsgfh36';
        req.httpMethod = 'GET';
        RestContext.request = req;
        RestContext.response= res;
        
        Test.startTest();
        SampleREST.doGet();
        Test.stopTest(); 
        
        String responseBody = res.responseBody.toString();
        System.assertEquals('No account found with id 234576dsgfh36', responseBody);
        System.assertEquals(200, res.statusCode);
        
    }

Unit Testing POST Methods

Good news: The unit tests for a REST API POST call are very similar to the unit tests for a REST API GET call. The main difference is that here the RestRequest includes a “requestBody”.

A positive unit test for a REST API POST call will examine the creation of a Salesforce account record. The name and description of the account are set in an instance of a wrapper class used to pass the information into the body of the RestRequest. Next, we retrieve the resulting account record in Salesforce via SOQL and assert that the record details match the values from the body of the RestResponse.

@isTest //positive unit test for the REST API POST call
    static void testPostPositive() {
        
        RestRequest req = new RestRequest(); 
        RestResponse res = new RestResponse();

        SampleREST.NewAccountRequest wrapper = new SampleREST.NewAccountRequest();
        wrapper.name = 'Ismail Test Post';
        wrapper.description = 'This is a test account';
        
        req.requestURI = '/services/apexrest/Account/';
        req.httpMethod = 'POST';
        req.requestBody = Blob.valueOf(JSON.serializePretty(wrapper));
        
        RestContext.request = req;
   	RestContext.response= res;
        
        Test.startTest();
        SampleREST.doPost();
        Test.stopTest();
        
        Account newAccountCreated = [SELECT Id, Name FROM Account WHERE Name = 'Ismail Test Post'];
        
        SampleREST.AccountWrapper responseBody = (SampleREST.AccountWrapper)JSON.deserialize(res.responseBody.toString(), SampleREST.AccountWrapper.class);
        System.assertEquals(newAccountCreated.Id, responseBody.accid);
        System.assertEquals(newAccountCreated.Name, responseBody.accName);
        System.assertEquals(201, res.statusCode);
    }

Finally, let’s write a negative unit test for a REST API POST call. This time in the wrapper class sent to the RestRequest we will include null parameters. In Salesforce, we know to successfully create an account you must provide an account name and so when we pass in null we can be sure we will get back an HTTP status code of 500 (An Internal Server Error).

@isTest //negative unit test for the REST API POST call
    static void testPostNegative() {
        
        RestRequest req = new RestRequest(); 
        RestResponse res = new RestResponse();

        SampleREST.NewAccountRequest wrapper = new SampleREST.NewAccountRequest();
        wrapper.name = null;
        wrapper.description = null;
        
        req.requestURI = '/services/apexrest/Account/';
        req.httpMethod = 'POST';
        req.requestBody = Blob.valueOf(JSON.serializePretty(wrapper));
        
        RestContext.request = req;
   	RestContext.response= res;
        
        Test.startTest();
        SampleREST.doPost();
        Test.stopTest();
        
        System.assertEquals(500, res.statusCode);
    }

Summary

The above examples for unit testing RESTful Apex Methods using HTTP GET and POST calls demonstrate the value and benefits of test setup methods, .startTest() and .stopTest() methods, meaningful assertions and both positive and negative test scenarios. I hope that this will form a solid jumping off point for you when approaching your own unit tests for RESTful Apex Methods. Wishing you much success in your Salesforce REST API Integrations. If you enjoyed this topic, then you might also enjoy the Apex Web Services Unit on Trailhead.

    Contact Us

    Any questions, or comments get in touch below.