I have built an Apex integration with a customer's custom API. All API responses are of the form:
{
"code": 3000,
"message": "SUCCESS",
"data": { "some object": "JSON data" }
}
When there is an error, the code
value is something other than 3000
, and "message" contains the error message.
As part of the integration, we will have a daily Scheduled job which makes a simple API call as a "heartbeat" job. It looks like this:
public with sharing class APIPlatformHeartbeat implements Schedulable, Database.AllowsCallouts {
public void execute(SchedulableContext ctx) {
callApi();
}
public static void runNow() { // for ad-hoc runs
callApi();
}
// @Future required to make callout from scheduled (non-batch) job
@Future(Callout=true)
static void callApi() {
LogUtil logger = new LogUtil();
try {
API_Platform_Integration_Settings__c settings = API_Platform_Integration_Settings__c.getOrgDefaults();
APIPlatform.Connection conn = APIPlatform.connect(settings.Named_Credential__c, logger);
conn.getGranularities();
}
catch (APIPlatform.APIPlatformException e) {
logger.logError(e.getMessage(), e.response);
throw e;
}
catch(Exception e) {
logger.logException(e);
throw e;
}
finally {
logger.publish();
}
}
}
Note that that call to conn.getGranularities()
will throw an APIPlatform.APIPlatformException
if the API response contains an non-3000
response code. This method handles that and logs the exception if needed. But it's not working when I test it. Here's the unit test:
@IsTest
static void testRunNow_Fail() {
TestObjectFactory.mockSingleResource('api_error');
Test.startTest();
APIPlatformHeartbeat.runNow();
Test.stopTest();
List<Error_Log__c> res = [SELECT Id, Message__c FROM Error_Log__c WHERE Type__c = :LogUtil.LOG_TYPE_ERROR];
System.assertEquals(1, res.size());
System.assertEquals('An internal service error occurred.', res[0].Message__c);
}
The call to TestObjectFactory.mockSingleResource('api_error');
just mocks the response with a static resource that contains a simple error response. This should cause the exception to be thrown, then caught in the future method, and logged. However, when I run the unit test, the test dies with:
12:07:24.4 (93391748)|FATAL_ERROR|APIPlatform.APIPlatformException: An internal service error occurred.
That exception is a custom exception, so should be catchable. I'm guessing this has something to do with future context, but I'm not aware of any changes to exception handling in future methods.
APIPlatformHeartbeat
after catching them. That'll cause the exception to bubble up until it reaches your test method (and then your test dies because of the uncaught, re-thrown exception). Is the re-throwing of exceptions a behavior that you want/need to keep?@future
to place nice withSchedulable
. No longer needed with the logging in place, and I completely overlooked it. Please add as an answer so I can accept.