2

So I've been through quite a few posts/questions already looking for enough information to have this "click" for me, but I'm still missing something. This is my first time working with RestResource outside of Trailhead. Essentially I'm getting a good-sized JSON payload delivered via a webhook to my REST service. After reading a ton of posts I figured out how to use a ResponseWrapper and classes to deserialize my JSON. Using SF Workbench's REST explorer, I can successfully send my payload and see a response returned.

Eventually, I'll need to manipulate the data and upsert(PUT) it into my DB, but I wanted to get some basic testing set up as I know it can be a pain and is essential to migrating to production.

Below is my Class and attempt at a Test Class. I guess what I'm not understanding is how I can pass my sample JSON via the test and then get code coverage. I thought I should at least be able to get a System.AssertEquals(200, res.statusCode) to work but doesn't seem to do anything... right now except run indefinitely until I abort the test. Do I need to use a mock class? Any help is greatly appreciated.

    @RestResource(urlMapping='/Leads/v1/*')
    global with sharing class LeadManager {
    
    global class ResponseWrapper {
        public Integer contactId;
        public Integer campaignId;
    }
    
    @HttpPost
    global static ResponseWrapper doPost(JSONLead lead) {
       ResponseWrapper resp = new ResponseWrapper();
       resp.contactId = lead.id;
       resp.campaignId = lead.campaign_id;
       //map a bunch more variables
       return resp;
    }
    
    global class JSONLead {
        global Integer id;
        global Integer contact_id;
        global Integer campaign_id;
        global JSONContact contact;
        global JSONCall call;
        // define more vars
    }

    global class JSONContact {
        global Integer id;
        global String first_name;
        global String last_name;
        // define more vars
    }
    
    global class JSONCall {
        public String occured_at;
        public Integer duration;
        public String recording_url;
    }
}

And then my test class attempt

@IsTest
private class LeadManagerTest {
    @isTest static void testDoPost() {
        String testJson = '{'+
        '   \"lead\":{'+
        '      \"id\":912873,'+
        '      \"contact_id\":8389,'+
        '      \"campaign_id\":1,'+
        '      \"contact\":{'+
        '         \"id\":8389,'+
        '         \"first_name\":\"Homer\",'+
        '         \"last_name\":\"Simpson\",'+
        '      },'+
        '      \"call\":{'+
        '         \"occured_at\":\"2017-05-30T08:18:49.000-07:00\",'+
        '         \"duration\":52,'+
        '         \"recording_url\":\"http://linktofile.com/somefile.mp3\"'+
        '      }'+
        '   }'+
        '}';
        
        //Send the test JSON via POST and then check for a valid response. I thought I could do something like
        LeadManager.ResponseWrapper respWrap = new LeadManager.doPost(testJson);
        System.assert(respWrap != null);
        // but I get Invalid type: LeadManager.doPost error

        //also tried fooling around a bit with with RestRequest/Response but with no luck...
        RestRequest req = new RestRequest();
        RestResponse res = new RestResponse();
        req.requestURI = '/services/apexrest/Leads/v1/';
        req.httpMethod = 'POST';
        RestContext.request = req;
        req.requestBody = Blob.valueOf(testJson);
        RestContext.response = res;
        System.assertEquals(200, res.statusCode);
        
    }
    
}

1 Answer 1

2

Not sure why you'd get an "infinite loop" run, but you've looked at the wrong example(s). You use the RestContext method when your main class uses it. For a parameterized call (e.g. @HttpPost), you call the method directly, just as you'd test any other method. You'd do something like this:

LeadManager.JSONLead testLead = (LeadManager.JSONLead)JSON.deserialize(testJSON, LeadManager.JSONLead.class);
Test.startTest();
LeadManager.ResponseWrapper result = LeadManager.doPost(testLead);
Test.stopTest();
/* Verify the result is correct */
System.assert( ... );

Edit: I just saw your edit; you do not use new to call static methods. You only use new to create new objects in memory. This may have been your mistake.

7
  • Yes! This is the answer I needed. Wish I would have asked two days ago. Thanks a ton. Can you explain to me the use of parentheses around (LeadManager.JSONLead) before deserializing it?
    – Jack S
    Jan 11, 2021 at 19:04
  • 2
    This is called casting. You use this to tell the compiler that a given object is actually a different object. JSON.deserialize returns an Object, so we have to tell the compiler it's "real" type. See also my answer here that goes in to more detail.
    – sfdcfox
    Jan 11, 2021 at 19:10
  • Thanks. One more follow up if you don't mind. Since the value is being passed via testJSON, I should be able to do System.assertEquals(1, result.campaignId); correct? That is failing and telling me the Actual is Null.
    – Jack S
    Jan 11, 2021 at 19:21
  • 1
    @JackS Oh, your JSON would be different; when you call the method (via /services/apexrest...), you need the outer { "lead": ... }, but since you're deserializing directly to the LeadManager.JSONLead object, that bit needs to be removed from the JSON.
    – sfdcfox
    Jan 11, 2021 at 19:25
  • 1
    @JackS You'll want to read about database isolation in unit tests. You need to create a test record for your method to discover via query. This design choice prevents you from writing tests that will fail in one org (e.g. a sandbox) but then pass in another (e.g. production), as well as reducing the reliance of your tests on data that may be changed or deleted later. Also search for test-data-isolation.
    – sfdcfox
    Jan 11, 2021 at 21:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .