Deliver Test Event Messages
Deliver Test Event Messages After Test.stopTest()
To publish platform event messages in an Apex test, enclose the publish statements within Test.startTest() and Test.stopTest() statements. Call the EventBus.publish() method within the Test.startTest() and Test.stopTest() statements. In test context, the EventBus.publish() method enqueues the publish operation. The Test.stopTest() statement causes the event publishing to be carried out and event messages to be delivered to the test event bus. Include your validations after the Test.stopTest() statement. For example, you can validate that a subscribed Apex trigger or a subscribed flow Pause element has performed the expected actions, like creating a Salesforce record.
// Create test events
Test.startTest();
// Publish test events with EventBus.publish()
Test.stopTest();
// Perform validations
Example
This sample test class contains two test methods. The testValidEvent() method checks that the event was successfully published and fires the associated trigger. The testInvalidEvent() method verifies that publishing an event with a missing required field fails, and no trigger is fired. The testValidEvent() method creates one Low_Ink__e event. After Test.stopTest(), it executes a SOQL query to verify that a case record is created, which means that the trigger was fired. The second test method follows a similar process but for an invalid test.
This example requires that the Low_Ink__e event and the associated trigger are defined in the org.
@isTest
public class EventTest {
@isTest static void testValidEvent() {
// Create a test event instance
Low_Ink__e inkEvent = new Low_Ink__e(Printer_Model__c='MN-123',
Serial_Number__c='10013',
Ink_Percentage__c=0.15);
Test.startTest();
// Publish test event
Database.SaveResult sr = EventBus.publish(inkEvent);
Test.stopTest();
// Perform validations here
// Verify SaveResult value
System.assertEquals(true, sr.isSuccess());
// Verify that a case was created by a trigger.
List<Case> cases = [SELECT Id FROM Case];
// Validate that this case was found
System.assertEquals(1, cases.size());
}
@isTest static void testInvalidEvent() {
// Create a test event instance with invalid data.
// We assume for this test that the Serial_Number__c field is required.
// Publishing with a missing required field should fail.
Low_Ink__e inkEvent = new Low_Ink__e(Printer_Model__c='MN-123',
Ink_Percentage__c=0.15);
Test.startTest();
// Publish test event
Database.SaveResult sr = EventBus.publish(inkEvent);
Test.stopTest();
// Perform validations here
// Verify SaveResult value - isSuccess should be false
System.assertEquals(false, sr.isSuccess());
// Log the error message
for(Database.Error err : sr.getErrors()) {
System.debug('Error returned: ' +
err.getStatusCode() +
' - ' +
err.getMessage()+' - '+err.getFields());
}
// Verify that a case was NOT created by a trigger.
List<Case> cases = [SELECT Id FROM Case];
// Validate that this case was found
System.assertEquals(0, cases.size());
}
}
Deliver Test Event Messages on Demand with Test.getEventBus().deliver()
You can control when test event messages are delivered to subscribers by calling Test.getEventBus().deliver(). Use Test.getEventBus().deliver() to deliver test event messages multiple times, and verify that subscribers have processed the test events each step of the way. Delivering event messages multiple times is useful for testing sequential processing of events. For example, you can verify sequential actions of a subscriber in a loop within the same test.
Enclose Test.getEventBus().deliver() within the Test.startTest() and Test.stopTest() statement block.
Test.startTest();
// Create test events
// ...
// Publish test events with EventBus.publish()
// ...
// Deliver test events
Test.getEventBus().deliver();
// Perform validations
// ...
Test.stopTest();
Also, you can call Test.getEventBus().deliver() in an Apex test method outside the Test.startTest() and Test.stopTest() statement block. Doing so enables you to test event messages with asynchronous Apex.
Test.startTest();
// Do some tests
Test.stopTest();
// Deliver test events
Test.getEventBus().deliver();
Deliver Test Event Messages Published from Asynchronous Apex
When testing a batch Apex job that publishes BatchApexErrorEvent on failure, use the Test.startTest() and Test.stopTest() statement block with Test.getEventBus().deliver(). The Test.stopTest() call ensures that the asynchronous Apex job executes after this statement. Next, Test.getEventBus().deliver() delivers the event message that the failed batch job published.
This snippet shows how to execute a batch Apex job and deliver event messages. It executes the batch job after Test.stopTest(). This batch job publishes a BatchApexErrorEvent message when a failure occurs through the implementation of Database.RaisesPlatformEvents. After Test.stopTest() runs, a separate Test.getEventBus().deliver() statement is added so that it can deliver the BatchApexErrorEvent.
try {
Test.startTest();
Database.executeBatch(new SampleBatchApex());
Test.stopTest();
// Batch Apex job executes here
} catch(Exception e) {
// Catch any exceptions thrown in the batch job
}
// The batch job fires BatchApexErrorEvent if it fails, so deliver the event.
Test.getEventBus().deliver();
Asynchronous Apex also includes queueable Apex and future methods. If a platform event message is published from within those async Apex jobs, they’re delivered after Test.stopTest(). It’s not necessary to add Test.getEventBus().deliver();. The next example shows how to deliver a platform event message that a queueable Apex job publishes. After Test.stopTest(), the queueable job is executed and the event message is delivered.
Test.startTest();
System.enqueueJob(new SampleQueueableApex());
Test.stopTest();
// Queueable Apex job executes here.
// The platform event message published by the job is delivered too.
Example: Deliver Event Messages Individually
This test class publishes an Order_Event__e event message and delivers it using Test.getEventBus().deliver(). It verifies that the trigger processed the event message and created a task. A duplicate event message (an event with the same Event_ID__c custom field value) is published and delivered. The test verifies that the trigger didn’t create a task for the duplicate event.
Before you can run this test class, define a platform event with the name of Order_Event__e and these fields: Event_ID__c of type Text, Order_Number__c of type Text, Has_Shipped__c of type Checkbox. Also, in the OrderTrigger trigger, replace the user Name field value in the SOQL query with a user’s full name in your Salesforce org, such as John Smith.
@isTest
public class MyTestClassDeliver {
@isTest static void doSomeTesting() {
Test.startTest();
// Publish a test event
Order_Event__e event = new Order_Event__e(
Event_ID__c='123AB', Order_Number__c='12346', Has_Shipped__c=true);
Database.SaveResult sr = EventBus.publish(event);
// Verify that the publish was successful
System.assertEquals(true, sr.isSuccess());
// Deliver the test event before Test.stopTest()
Test.getEventBus().deliver();
// Check that the case that the trigger created is present.
List<Task> tasks = [SELECT Id FROM Task];
// Validate that this task was found.
// There is only one test task in test context.
Integer taskCount = tasks.size();
System.assertEquals(1, taskCount);
// Publish a duplicate event
Order_Event__e dupEvent = new Order_Event__e(
Event_ID__c='123AB', Order_Number__c='12346', Has_Shipped__c=true);
Database.SaveResult sr2 = EventBus.publish(dupEvent);
// Verify that the publish was successful.
System.assertEquals(true, sr2.isSuccess());
Test.getEventBus().deliver();
// Get all tasks in test context
List<Task> tasksNew = [SELECT Id FROM Task];
// Validate that no task was created and
// the number of tasks should not have changed.
System.assertEquals(taskCount, tasksNew.size());
Test.stopTest();
}
}
This example trigger processes Order_Event__e event messages that the test class publishes.
trigger OrderTrigger on Order_Event__e (after insert) {
// List to hold all cases to be created.
List<Task> tasks = new List<Task>();
// Get user Id for case owner
User usr = [SELECT Id FROM User WHERE Name='<Replace with FirstName LastName>' LIMIT 1];
// Iterate through each notification.
for (Order_Event__e event : Trigger.New) {
if (event.Has_Shipped__c == true) {
// Create task only if it doesn't exist yet for the same order
String eventID = '%' + event.Event_ID__c;
List<Task> tasksFromQuery =
[SELECT Id FROM Task WHERE Subject LIKE :eventID];
if (tasksFromQuery.size() == 0) {
Task t = new Task();
t.Priority = 'Medium';
t.Subject = 'Follow up on shipped order ' + event.Order_Number__c +
' for event ID ' + event.Event_ID__c;
t.OwnerId = usr.Id;
tasks.add(t);
}
}
}
// Insert all tasks in the list.
if (tasks.size() > 0) {
insert tasks;
}
}
Fail the Publishing of Event Messages to Test Apex Publish Callbacks
Apex publish callbacks contain the final result of asynchronous EventBus.publish calls. To test your Apex publish callback class, you can simulate the failure of a publish call with Test.getEventBus().fail().
In an Apex test, event messages are published synchronously in the test event bus. To can simulate the execution of the callback methods in a test, you can deliver or fail the publishing of the event messages. This section covers the failure of event publishing.
The Test.getEventBus().fail() method causes the publishing of events to fail immediately after the call and event messages are removed from the test event bus. This method causes the onFailure() method in the callback class to be invoked. When the event messages fail to publish, none of the triggers defined on the platform event receive any failed events.
This example class is a test class for the FailureAndSuccessCallback class that is given in Get the Result of Asynchronous Platform Event Publishing with Apex Publish Callbacks. This test class shows how to test the failed delivery of test event messages in the test event bus. Before you run this test class, define a platform event in Setup with the label Order Event and a Text field of Order Number.
@isTest
public class MyCallbackTest {
@isTest static void testFailedEventsWithFail() {
// Publish with callback
FailureAndSuccessCallback cb = new FailureAndSuccessCallback();
// Create test event with EventUuid field value
Order_Event__e event = (Order_Event__e)Order_Event__e.sObjectType.newSObject(null, true);
event.Order_Number__c='100';
System.debug('EventUuid of created event: ' + event.EventUuid);
// Publish an event with callback
EventBus.publish(event, cb);
// Fail event
// (invoke onFailure and DO NOT deliver event to subscribers)
Test.getEventBus().fail();
// Verify that tasks were created by the onFailure() method
List<Task> tasksFailed =
[SELECT Id,Subject,Description FROM Task
WHERE Subject='Follow up on event publishing failures.'];
System.Assert.areEqual(1,tasksFailed.size(),
'Unexpected number of tasks received for failed publishing');
System.debug('tasksFailed[0].Description=' + tasksFailed[0].Description);
System.debug('event.EventUuid=' + event.EventUuid);
System.Assert.isTrue(tasksFailed[0].Description.contains(event.EventUuid),
'EventUuid was not found in the Description field.');
}
}
To deliver event messages successfully, check out these sections.