Find your content:

Search form

You are here

Writing Test Classes for Apex RestService

 
Share

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

I noted some very interesting things while experimenting. Before doing anything, I just did a google search 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 a few lines and ran and it. I still achieved enough test coverage so my question is why do I need to simply instantiate the 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 behavior? Also what are the best practices for writing Test classes for apex Rest services?


Attribution to: Mohith Shrivastava

Possible Suggestion/Solution #1

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.


Attribution to: Andrew Fawcett
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/4988

My Block Status

My Block Content