I am integrating with a third-party API that calls an unauthenticated webhook (HTTP Post) that I have implemented with a small payload; my code then uses that payload to make an authenticated call to the remote API to fetch the sensitive data associated with the notification. For various reasons, we have implemented the inbound HTTP Post by publishing platform events. There is a trigger that fires on the event; it is responsible for making the callout to the remote API and taking action on the result.
I can test all of the pieces in separation, but when I attempt to test the Trigger by publishing an event, I get a System.CalloutException: Callout from triggers are currently not supported
error, even though I have mocked the HTTP request. I know the mocking works, as I have other tests which use the same mocking setup and call the Trigger handler directly. I'm guessing that when the EventBus fires the event in test context, the Trigger must run in a different context which does not see the HttpCalloutMock
that I've setup. And yes, I have wrapped the EventBus.publish()
call in Test.startTest()
/Test.stopTest()
, and I have tried manually delivering the event with Test.getEventBus().deliver()
. Here's my current test method:
@IsTest
static void testTrigger() {
setupMocks(); // used by all test which make callouts
MY_Platform_Event__e evt = new MY_Platform_Event__e(
EventType__c = 1,
PackageId__c = '717cda19-b348-447c-96b4-018ac806be13',
MessageId__c = '717cda19-b348-447c-96b4-018ac806be13\\1',
Description__c = 'Package.Create',
Environment_Token__c = ENV_TOKEN
);
Test.startTest();
Database.SaveResult sr = EventBus.publish(evt);
System.assertEquals(true, sr.isSuccess());
Test.getEventBus().deliver();
Test.stopTest();
}
I really only need the required 1% code coverage for my trigger, as all of the handler logic is tested elsewhere in isolation. Because the whole point of the Trigger is to call the remote API, however, I can't construct even a minimum test that will successfully fire the trigger, unless I add an Test.isRunningTest()
check inside the Trigger handler. I can do so if needed, but the need for such a test is a red flag to me that I need to check my assumptions. Is it possible to test a Platform Event Trigger that makes an HTTP callout, without using Test.isRunningTest()
to skip the callout?
edit to add: upon reflection, I can't even use Test.isRunningTest()
in the handler, since the handler is called directly in other tests. I can either check Test.isRunningTest()
directly in the Trigger, or use a @TestVisible private Boolean
flag in the handler to allow a test method to turn off the callout code... neither of which approaches do I like.
My_Platform_Event__e
, it needs to delegate to an async transaction the actual callout.