As end to end test could not be possible with testing trigger and batch separately, another workaund could be is to call the execute's code as future. Still not perfect but for the current scenario atleast I am able to do an end to end test, following is an example- Test class: @IsTest public class TestAccountTrigger { @IsTest private static void testBatchFunctionality() { // create test data Test.startTest(); // set mock // update records Test.stopTest(); // verify records as processed by batch } } Trigger: trigger AccountTrigger on Account (after update) { if(myCondition) { if(Test.isRunningTest()) { // call batch's method as future as Database.executeBatch is counted as DML MyBatchClass.executeCalloutAsFuture(Trigger.newMap.keySet()); } else { Database.executeBatch(new MyBatchClass(), 1); } } } Batch apex: public class MyBatchClass implements Database.Batchable<SObject>, Database.AllowsCallouts { public Set<Id> accountSet = new Set<Id>(); public OrderPABXBatch(Set<Id> accountSet) { this.accountSet = accountSet; } // start method public Database.QueryLocator start(Database.BatchableContext BC){ return getQueryLocator(this.accountSet); } private Database.QueryLocator getQueryLocator(Set<Id> accountSet) { return Database.getQueryLocator([SELECT Id FROM Account WHERE Id IN :accountSet]); } // call execute method from test context @future(callout=true) public static void executeCalloutAsFuture(Set<Id> orderSet) { MyBatchClass batchInstance = new MyBatchClass(orderSet); // Get an iterator Database.QueryLocatorIterator iterator = batchInstance.getQueryLocator().iterator(); List<Account> accounts = new List<Account>(); // Collect the first record if(iterator.hasNext()) { accounts.add((Account) iterator.next()); } if(!accounts.isEmpty()) { batchInstance.execute(null, accounts); } } // only relevant execute method public void execute(Database.BatchableContext batchableContext, List<Account> accounts) { for(Account account: accounts) { // make http request callout // based on response update account } } }