30

I was writing test classes for my apex Rest Service that i had built for an ipad app.

I noted very interesting things while experimenting.Before anything i just did a google and found an interesting blog post by Jeff

http://blog.jeffdouglas.com/2012/03/21/writing-unit-tests-for-v24-apex-rest-services/

@RestResource(urlMapping='/v.9/member/*/results/*') 
global with sharing class MemberRestSvc {

@HttpGet 
global static ReturnClass doGet() {

String[] uriKeys = RestContext.request.requestURI.split('/');
// get the member name from the uri
String memberName = uriKeys.get(uriKeys.size()-3);

// do awesome programming stuff here & catch any exceptions
try {

  List<Contact> contacts = [Select Id From Contact where member_name__c = :memberName];
  return new ReturnClass('true', 'Query executed successfully.', contacts);

} catch (Exception e) {
  return new ReturnClass('false', e.getMessage(), null);
}

 }

 global class ReturnClass {

global String success;
global String message;
global List<Contact> records;

  global ReturnClass(String success, String message, List<Contact> records) {
  this.success = success;
  this.message = message;
  this.records = records;
 }

}

}

And here is the test class

@isTest
private class Test_MemberRestSvc {

static {
// setup test data  
}

static testMethod void testDoGet() {

RestRequest req = new RestRequest(); 
RestResponse res = new RestResponse();

req.requestURI = 'https://cs9.salesforce.com/services/apexrest/v.9/member/me/results/today';  
req.httpMethod = 'GET';
RestContext.request = req;
RestContext.response = res;

MemberRestSvc.ReturnClass results = MemberRestSvc.doGet();

System.assertEquals('true', results.success);
System.assertEquals(10, results.records.size());
System.assertEquals('Query executed successfully.', results.message);

}

}

I just commented few lines and ran and still i achieved test coverage so my question is why do i need to simply instantiate Rest request and response objects since i am directly calling the class

  @isTest
  private class Test_MemberRestSvc {

  static {
    // setup test data  
  }

static testMethod void testDoGet() {

/*RestRequest req = new RestRequest(); 
RestResponse res = new RestResponse();

req.requestURI =    'https://cs9.salesforce.com/services/apexrest/v.9/member/me/results/today';  
req.httpMethod = 'GET';
RestContext.request = req;
RestContext.response = res;
*/Commented the code meant for REST apex intialization and merely calling the method!!

MemberRestSvc.ReturnClass results = MemberRestSvc.doGet();

System.assertEquals('true', results.success);
System.assertEquals(10, results.records.size());
System.assertEquals('Query executed successfully.', results.message);

}

}

Can someone explain this behaviour?Also what are the best practices for writin Test classes for apex Rest services ?

32

Setting up the RestContext manually in this case is a bit like Test.setCurrentPage if your Apex Controller code looks for the current page for what it needs. And so in this case it helps emulate what your service is expecting at runtime.

In the case of designing Rest services, they receive parameters via a number of inputs. A key one is the URI (as parts of this are often dynamic to contextualise the call much like a web page URL). In this case the member_name_c filter is extracted from the URI (you can also use URI parameters, see the docs on RestRequest methods for a note on testing).

So yes you can call the method direct, but if it needs any context (via its own references to RestContext) you will still need the RestContext setup before you call the methods. You mention above that you obtain the same code coverage, do the asserts still pass, as it looks like if no member_name__c filter is resolved from the URI it would throw an exception and thus the assertions relating to the response should fail?

Thoughts on Best Practice: As regards best practice, since Apex REST services now handle most Apex types, for more rich servies you create via annotations. The platform then handles all the serialisation and deserialisation for you. I don't see there is much need to really use RestContext beyond using it to emulate the URI (and any parameters on it if you need this) the client is calling you on. The rest of the time, as per standard Apex Unit tests you can just pass in parameters directly and handling responses and exceptions directly from the methods you write in the standard way.

Manual REST Services: That said, if you do want to perform your own low level serialisation and deserialisation by handling this in your Apex REST method. For example custom exception handling to the REST response or some serialisation of your own. The RestContext will be very useful both before and after to test these kind of services. However I would say unless you have some very good reason, you should use the excellent default behaviour of simply expressing your needs via the method signature with annotations.

  • Great points Andrew !Yes if we are using parameters i see that using Context is an advantage .very Valid thoughts.Thanks again – Mohith Shrivastava Dec 2 '12 at 11:31
  • also asserts passed even without initialization of context . – Mohith Shrivastava Dec 2 '12 at 11:37
  • The RestContext link appears to be broken. Is this the right link? salesforce.com/us/developer/docs/apexcode/… – edgartheunready Feb 17 '14 at 16:40
  • 1
    Yes it does look like it has become broken, i've fixed it with the latest, thanks! – Andrew Fawcett Feb 17 '14 at 22:07

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.