I have a batch class which implements the Database.RaisesPlatformEvents interface. This interface has recently been introduced and helps log unexpected exceptions: https://developer.salesforce.com/docs/atlas.en-us.218.0.object_reference.meta/object_reference/sforce_api_objects_batchapexerrorevent.htm
It works well and I have implemented a trigger which calls a handler class to log the error permanently in a separate object:
trigger LogBatchApexErrorEvent on BatchApexErrorEvent (after insert){ if(Trigger.IsInsert && Trigger.IsAfter){ LogBatchApexErrorEvent_Handler.persistLog(Trigger.New); } }
My test batch class looks somewhat like this:
public with sharing class TestBatch implements Database.Batchable<sObject>, Database.RaisesPlatformEvents { public Database.QueryLocator start(Database.BatchableContext BC){ String query = 'SELECT Id FROM Account'; return Database.getQueryLocator(query); } public void execute(Database.BatchableContext BC, List<SObject> scope){ Integer i = 1 / 0; } public void finish(Database.BatchableContext BC){ } }
The problem is, it seems impossible to write a test class for this. I currently have this test method:
static testMethod void testBatchApexErrorEvent() { insert new Account(Name = 'Test Account'); try { Test.startTest(); TestBatch tb = new TestBatch(); Database.executeBatch(tb); Test.stopTest(); } catch(System.MathException e){} System.assertEquals(1, [SELECT Id FROM Log__c].size()); }
However, the BatchApexErrorEvent is a Platform Event so is asynchronous – which means that at the time my
System.assertEquals
line runs it hasn’t actually fired yet.I also tried throwing a BatchApexErrorEvent manually, but I get the following error: DML operation Insert not allowed on BatchApexErrorEvent. This is my attempt:
BatchApexErrorEvent event = new BatchApexErrorEvent(); EventBus.publish(event);
Despite this being the way BatchApexErrorEvents are tested in this blogpost on the topic, it doesn’t seem possible anymore: https://developer.salesforce.com/blogs/2019/01/building-a-batch-retry-framework-with-batchapexerrorevent.html
Considering there is no way to either a) test two asynchronous processes in one test method or b) manually throw a BatchApexErrorEvent, is there currently any way for me to test this? 🙂
Answer
So as I can see from your code Divide by zero will throw an exception. Which inturn will fire BatchApexErrorEvent
. The thing we need to look for is this is the test context. The events will not deliver unless you force it to deliver.
Thus you have to call Test.getEventBus().deliver();
, this will ensure your triggers on platform events are fired and you can do successful assert.
static testMethod void testBatchApexErrorEvent() {
insert new Account(Name = 'Test Account');
try{
Test.startTest();
TestBatch tb = new TestBatch();
Database.executeBatch(tb);
Test.getEventBus().deliver();
Test.stopTest();
} catch(System.MathException e){}
Test.getEventBus().deliver();
System.assertEquals(1, [SELECT Id FROM Log__c].size());
}
Source: Deliver Test Event Messages on Demand with Test.getEventBus().deliver()
Attribution
Source : Link , Question Author : Koen Wesselman , Answer Author : Koen Wesselman