One way to unit test whether a Platform Event is published with expected contents (without using listeners) is to use the Unit of Work pattern. An implementation of this is contained in the GitHub repo apex-common aka fflib.
Your code looks like the following
fflib_ISobjectUnitOfWork uow = Application.UnitofWork.newInstance();
... construct the PlatformEvent here
uow.registerPublishAfterSuccessTransaction(myPlatformEvent);
...
uow.commitWork(); // this calls EventBus.publish()
and your testmethod mocks a UnitOfWork
(in my example, using ApexMocks although you could also use Amoss) and then verifies that the registerPublishAfterSuccessTransaction
was called with the expected argument
fflib_apexMocks mocks = new fflib_ApexMocks();
// Given mock UnitOfWork
fflib_SObjectUnitOfWork mockUow = (fflib_SObjectUnitOfWork) mocks.mock(fflib_SObjectUnitOfWork.class);
// Given mock UoW injected
Application.UnitOfWork.setMock(mockUow);
// when code under test invoked
new MyClass().doStuff();
// then verify event constructed, constructed as expected, and published
((fflib_SObjectUnitOfWork)mocks.verify(mockUow,mocks.times(1)
.description('myEvent sb published')))
.registerPublishAfterSuccessTransaction(
fflib_Match.sObjectOfType(MyEvent__e__e.SObjectType)
);
((fflib_SObjectUnitOfWork)mocks.verify(mockUow,mocks.times(1)
.description('myEvent sb published')))
.registerPublishAfterSuccessTransaction(
fflib_Match.sObjectWith(new Map<SObjectField,Object> {
MyEvent__e.Field1__c => myExpectedField1Val,
MyEvent__e.Field2__c => myExpectedField2Val
})
);
((fflib_SObjectUnitOfWork)mocks.verify(mockUow,mocks.times(1)
.description('commitwork sb called')))
.commitWork();
Notes:
Since the above is based on the fflib
architecture, you need an Application.cls
. This will include an entry for MyEvent__e
in the UnitOfWork
factory as follows:
public static final fflib_Application.UnitOfWorkFactory UnitOfWork =
new fflib_Application.UnitOfWorkFactory(
new List<SObjectType> { // list in dependency order (e.g. Accounts before Contacts)
...
MyEvent__e.SObjectType
}
);
The approach above of course works for normal DML (registerNew(myObject)
, registerDirty(myObject)
, etc and even sending email (uow.registerEmail(email)
) so you can see if your DML and/or outbound apex email was constructed properly in a unit test.
EventBus.publish
in a mockable way - either a separate class you mock or as a virtual method you override in your test) for checking it gets published, and a separate unit test of the processing you initiate from the platform event subscriber - use best practice and put all of this in a trigger handler, not in the trigger.