End to end test could not be possible with batch class when it's methods are broken for unit test, another workaround is to call the execute's code as future; such that batch runs a future for unit tests. 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 } } } A private @TestVisible variable could be used instead of Test.isRunningTest(), just a matter of choice.