I have coded a batch class and am now working on the corresponding unit test. I believe I've inserted all the objects I need but none of the actual execute method is being covered, despite the test passing. The other lines of code are being passed. Here's my class:

global class LastIndexLockDateByLOBBatch implements Database.Batchable<sObject>, Database.Stateful, Schedulable {
    global String query = 'SELECT ID, Policy__r.Product_Name__r.Product_LOB__c FROM Policy_Allocation_Option__c WHERE Performance_Lock_Date__c = YESTERDAY AND' + 
                       '(LastModifiedDate = YESTERDAY OR LastModifiedDate = TODAY)';
global Set<Id> processedCons; //to save processed contact Ids from previous batch

public LastIndexLockDateByLOBBatch() {
    processedCons = new Set<Id>();

public Database.QueryLocator start(Database.BatchableContext BC) {       
    return Database.getQueryLocator(query);

public void execute(Database.BatchableContext BC, List<Policy_Allocation_Option__c> scope) { 
    Set<Id> conIdSet = new Set<Id>();
    Map<Id, Contact_Policy_Allocation__c> cpaByContactIdMap = new Map<Id, Contact_Policy_Allocation__c>();
    Map<Id, Policy_Allocation_Option__c> paoByIdMap = new Map<Id, Policy_Allocation_Option__c>(scope);
    for(Contact_Policy_Allocation__c cpa : [SELECT Id, Contact__c, Policy_Allocation_Option__c, Policy_Allocation_Option__r.Performance_Lock_Date__c FROM Contact_Policy_Allocation__c WHERE Policy_Allocation_Option__c IN :scope])            
        if(!processedCons.contains(cpa.Contact__c))  {  //skip already processed contacts
            cpaByContactIdMap.put(cpa.Contact__c, cpa);
    List<Contact> conList= new List<Contact>([SELECT Id, Last_Fixed_Index_Lock_Date__c, Last_Life_Index_Lock_Date__c, Last_VA_Lock_Date__c FROM Contact WHERE Id IN :conIdSet]);
    if(!conIdSet.isEmpty()) {
        for(Contact con :conList) {
            Contact_Policy_Allocation__c cpa = cpaByContactIdMap.get(con.Id);
            if (paoByIdMap.containsKey(cpa.Policy_Allocation_Option__c) && paoByIdMap.get(cpa.Policy_Allocation_Option__c).Policy__r.Product_Name__r.Product_LOB__c.Contains('Fixed')) {
                con.Last_Fixed_Index_Lock_Date__c = cpaByContactIdMap.get(con.Id).Policy_Allocation_Option__r.Performance_Lock_Date__c;
            if (paoByIdMap.containsKey(cpa.Policy_Allocation_Option__c) && paoByIdMap.get(cpa.Policy_Allocation_Option__c).Policy__r.Product_Name__r.Product_LOB__c.Contains('Life')) {
                con.Last_Life_Index_Lock_Date__c = cpaByContactIdMap.get(con.Id).Policy_Allocation_Option__r.Performance_Lock_Date__c;
            if (paoByIdMap.containsKey(cpa.Policy_Allocation_Option__c) && paoByIdMap.get(cpa.Policy_Allocation_Option__c).Policy__r.Product_Name__r.Product_LOB__c.Contains('VA')) {
                con.Last_VA_Lock_Date__c = cpaByContactIdMap.get(con.Id).Policy_Allocation_Option__r.Performance_Lock_Date__c;
        update conList;
        processedCons.addAll(conIdSet); //add processed contacts to this list, if same contact present in next batch, it will be skipped

public void finish(Database.BatchableContext BC) {
    system.debug('Final Processed : ' + processedCons); 

public void execute(SchedulableContext sc) {
    Database.executeBatch(this, 200);

And here is my test class:

public class LastIndexLockDateByLOBBatchTest {

public static testmethod void LastIndexLockDateByLOBBatchTest() {
    Account acc = new Account();
    acc.Name = 'TestAccount';
    insert acc;        
    Contact con = new Contact();
    con.FirstName = 'Test Name';
    con.LastName = 'Test';
    con.AccountId = acc.Id;
    con.Last_Fixed_Index_Lock_Date__c = System.today();
    insert con;
    Policy__c p = new Policy__c();
    p.Name = '71629012';
    insert p;
    Campaign_Member_Policy__c cmp = new Campaign_Member_Policy__c();
    cmp.Policy__c = p.Id;
    cmp.Contact__c = con.Id;
    insert cmp;
    Policy_Allocation_Option__c pao = new Policy_Allocation_Option__c();
    pao.Policy__c = p.Id;
    pao.CreatedDate = System.today()-2;
    pao.LastModifiedDate = System.today();
    pao.Performance_Lock_Date__c = System.today();
    insert pao;
    Contact_Policy_Allocation__c cpa = new Contact_Policy_Allocation__c();
    cpa.Contact__c = con.Id;
    cpa.Policy_Allocation_Option__c = pao.Id;
    cpa.Campaign_Member_Policy__c = cmp.Id;
    insert cpa;
    Master_Product__c mp1 = new Master_Product__c();
    mp1.Name = 'TP1';
    mp1.Description__c = 'Test Product 1';
    mp1.Product_Line__c = 'Fixed';
    mp1.Active__c = TRUE;
    insert mp1;
    Master_Product__c mp2 = new Master_Product__c();
    mp2.Name = 'TP2';
    mp2.Description__c = 'Test Product 2';
    mp2.Product_Line__c = 'Life';
    mp2.Active__c = TRUE;
    insert mp2;
    Master_Product__c mp3 = new Master_Product__c();
    mp3.Name = 'TP3';
    mp3.Description__c = 'Test Product 3';
    mp3.Product_Line__c = 'VA';
    mp3.Active__c = TRUE;
    insert mp3;
    LastIndexLockDateByLOBBatch lastIndexLock = new LastIndexLockDateByLOBBatch();
    String sch= '0 00 7 * * ? *';
    String jobid = System.schedule('Last Index Lock Date By LOB Batch', sch, lastIndexLock);

I can't seem to figure out why NONE of the execute class is being covered, not even the public void execute(Database.BatchableContext BC, List<Policy_Allocation_Option__c> scope) { line. Any insight or help is extremely appreciated. Thanks!

2 Answers 2


Your data doesn't match the query in start(), so there's no records to iterate over in execute().

Policy_Allocation_Option__c pao = new Policy_Allocation_Option__c();
pao.Performance_Lock_Date__c = System.today();

You set Performance_Lock_Date__c to today, but your query looks for

WHERE Performance_Lock_Date__c = YESTERDAY

Side note: without assertions about the behavior of the code, unit tests have very limited value.

  • @sfn00b this is addressed by this answer which is a wealth of useful tips
    – cropredy
    Dec 16, 2020 at 23:31
  • That doesn't really make sense, which makes me suspect it is a red herring for some other change.
    – David Reed
    Dec 17, 2020 at 2:20

Some other suggestions:

  • Your testmethod is attempting to run two async events: the schedulable and then the batchable. SFDC will only execute 1 async within a test.startTest-test.stopTest bracket
  • Your testmethod is constructing a CRON expression for no reason -- you don't need to verify that SFDC knows how to schedule jobs.

So ...

Redo the testmethod

 @Istest static void givenSchedulableVerifyBatchable() {
    // given mock data
    .. DML mock data such that batch start() finds it

    // given schedulable
    LastIndexLockDateByLOBBatch schedulable = new LastIndexLockDateByLOBBatch();

    // when executed by SFDC
    SchedulableContext sc;

    // Then verify actions of execute() and finish()  
    asserts go here

Also, your query can be simplified:

String query = 'SELECT ID, Policy__r.Product_Name__r.Product_LOB__c FROM Policy_Allocation_Option__c WHERE Performance_Lock_Date__c = YESTERDAY AND' + 
                   '(LastModifiedDate >= YESTERDAY)';

or, better still:

public Database.QueryLocator start(Database.BatchableContext BC) {       
    return Database.getQueryLocator([SELECT ID, Policy__r.Product_Name__r.Product_LOB__c 
                                      FROM Policy_Allocation_Option__c 
                                      WHERE Performance_Lock_Date__c = YESTERDAY AND
                                            LastModifiedDate >= YESTERDAY]);

and, unless you are doing managed packages, there is no reason to use global modifier, public is best practice

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .