function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Haseeb Ahmad 9Haseeb Ahmad 9 

System.NullPointerException: Attempt to de-reference a null object on line 33

Hi Everyone,

Looking for some help on how to fix this test class and code coverage issue as I am getting System.NullPointerException: Attempt to de-reference a null object on line 33.

Apex Class:
 
public class MqlService {
    public void setMqlDate(List<sObject> newRecords, Map<Id, sObject> oldRecordsById) {
        List<sObject> records = getOpenRecords(newRecords, oldRecordsById);
        if (records.isEmpty()) {
            return;
        }
        
        BusinessHours hours = [select id from BusinessHours where name = 'MQL Hours'];
        for (sObject record : records) {
            // using this BusinessHours method will set the time to NOW if it is within the business hours
            // otherwise it will set the time to the time the business hours start next
            record.put('Mql_Date__c', BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()));
        }
    }
    
    // returns a list of records that are either inserted as 'Open', or updated to 'Open'
    private List<sObject> getOpenRecords(List<sObject> newRecords, Map<Id, sObject> oldRecordsById) {
        List<sObject> filteredRecords = new List<sObject>();
        for (sObject newRecord : newRecords) {
            if (!recordIsOpen(newRecord)) {
                continue;
            }
            
            if (isInsert(newRecord, oldRecordsById)) {
                filteredRecords.add(newRecord);
                continue;
            }
            
            if (oldRecordsById != null) {
                sObject oldRecord = oldRecordsById.get((Id)newRecord.get('id'));                
                if (oldRecord != null && !recordIsOpen(oldRecord)) {
                    filteredRecords.add(newRecord);                    
                }
            }
        }
        
        return filteredRecords;
    }
    
    private boolean isInsert(sObject newRecord, Map<Id, sObject> oldRecordsById) {
        return oldRecordsById == null || !oldRecordsById.containsKey((Id)newRecord.get('id'));
    }
                
    private boolean recordIsOpen(sObject record) {
        if (record.getSObjectType() == Lead.getSObjectType()) {
            return record.get('Status') == 'Open';
        } else if (record.getSObjectType() == Contact.getSObjectType()) {
            return record.get('Lead_Status__c') == 'Open';
        } else {
            return false;
        }
    }      
}

Test Class:
 
@isTest
public class MqlServiceTest {
    public static testMethod void testSetMqlDate() {
        // retrieve business hours
        BusinessHours hours = [select id from BusinessHours where name = 'MQL Hours'];
        
        // account for contact
        Account account = new Account(name = 'test');
        insert account;

		// lead / contact to update as Open
		Lead updateLead = new Lead(lastName = 'test', company = 'test', email = 'test@test.com', status = 'Pre-MQL');
        Contact updateContact = new Contact(lastName = 'test', accountId = account.id, lead_status__c = 'Pre-MQL');
        insert new List<sObject>{updateLead, updateContact};
            
        // update lead and contact
        updateLead.status = 'Open';
        updateContact.lead_status__c = 'Open';
            
        // lead / contact to insert as Open
        Lead insertLead = new Lead(lastName = 'test', company = 'test', email = 'test@test.com', status = 'Open');
        Contact insertContact = new Contact(lastName = 'test', accountId = account.id, lead_status__c = 'Open');            
            
        // capture the current time to check against business hours, set the time in time utils
        TimeUtils.setNow(Datetime.now());            
        
        // upsert list of leads / contacts to insert / update
        upsert new List<Lead>{insertLead, updateLead};
        upsert new List<Contact>{insertContact, updateContact};
        
        // assert the hour / minutes are correct, since the BusinessHours.nextStartDate doesn't use seconds
        for (Lead lead : [select mql_date__c from lead]) {
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).hour(), lead.mql_date__c.hour());
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).minute(), lead.mql_date__c.minute());            
        }
        
        for (Contact contact : [select mql_date__c from contact]) {
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).hour(), contact.mql_date__c.hour());
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).minute(), contact.mql_date__c.minute());            
        }
    }
}

Because of line 33 assert method filing calls is not getting any code coverage as well.

Thank you for your help. ​​​​​​​
Best Answer chosen by Haseeb Ahmad 9
ravi soniravi soni
Hi Haseeb,
try following Test Class
@isTest
public class MqlServiceTest {
    public static testMethod void testSetMqlDate() {
        // retrieve business hours
       MqlService MS = new MqlService();
       
        List<sObject> lstSobject = new List<sObject>();
        Map<Id, sObject> oldRecordsById = new Map<Id, sObject> ();
        
        BusinessHours hours = [select id from BusinessHours where name = 'MQL Hours'];
        
        // account for contact
        Account account = new Account(name = 'test');
        insert account;

		// lead / contact to update as Open
		Lead updateLead = new Lead(lastName = 'test', company = 'test', email = 'test@test.com', status = 'Open - Not Contacted');
        Contact updateContact = new Contact(lastName = 'test', accountId = account.id ,lead_status__c = 'Open');
         lstSobject.add(updateLead);
        lstSobject.add(updateContact);
        insert lstSobject; 
        MS.setMqlDate(lstSobject,null);
        oldRecordsById.put(updateLead.Id, updateLead);
        oldRecordsById.put(updateContact.Id, updateContact);
        MS.setMqlDate(lstSobject,oldRecordsById);
         
        
    }
}

In my org this test class coverage is 92%. try this.
let me know, if it's helps you and marking best of this so that it could helps to others.
Thank You

All Answers

ravi soniravi soni

hi Haseeb,
I review your test class and find out what is actual issue.
yes, you are right , assert method is failing because there is no BusinessHours  with this name MQL Hours so error is generating
first of all you try following query in query Editor .
select id,Name from BusinessHours where name = 'MQL Hours' Limit 1

if you are getting 0 records then create New Business Hours Record With this Name MQL Hours help with following Links.
https://help.salesforce.com/articleView?id=customize_supporthours.htm&type=5
and then replace following test class with your test class.
 

@isTest
public class MqlServiceTest {
    public static testMethod void testSetMqlDate() {
        // retrieve business hours
        BusinessHours hours = [select id from BusinessHours where name = 'MQL Hours' limit 1];
        
        // account for contact
        Account account = new Account(name = 'test');
        insert account;

		// lead / contact to update as Open
		Lead updateLead = new Lead(lastName = 'test', company = 'test', email = 'test@test.com', status = 'Pre-MQL');
        Contact updateContact = new Contact(lastName = 'test', accountId = account.id, lead_status__c = 'Pre-MQL');
        insert new List<sObject>{updateLead, updateContact};
            
        // update lead and contact
        updateLead.status = 'Open';
        updateContact.lead_status__c = 'Open';
            
        // lead / contact to insert as Open
        Lead insertLead = new Lead(lastName = 'test', company = 'test', email = 'test@test.com', status = 'Open');
        Contact insertContact = new Contact(lastName = 'test', accountId = account.id, lead_status__c = 'Open');            
            
        // capture the current time to check against business hours, set the time in time utils
        TimeUtils.setNow(Datetime.now());            
        
        // upsert list of leads / contacts to insert / update
        upsert new List<Lead>{insertLead, updateLead};
        upsert new List<Contact>{insertContact, updateContact};
        
        // assert the hour / minutes are correct, since the BusinessHours.nextStartDate doesn't use seconds
        for (Lead lead : [select mql_date__c from lead]) {
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).hour(), lead.mql_date__c.hour());
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).minute(), lead.mql_date__c.minute());            
        }
        
        for (Contact contact : [select mql_date__c from contact]) {
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).hour(), contact.mql_date__c.hour());
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).minute(), contact.mql_date__c.minute());            
        }
    }
}
let me know if it's helps you and close your query marking as solved.
Thank You
Haseeb Ahmad 9Haseeb Ahmad 9
Hi Veer,

I do have Business hours with the name 'MQL Hours'  and I am getting results even with or without limit 1. 

User-added image

This seems to something else even I change the query I am still getting the same exception. can you please check thank you so much for your help on this. 
Haseeb Ahmad 9Haseeb Ahmad 9
Hi Veer,

I changed the query on line 32 on test class to:

select mql_date__c from lead where mql_date__c != Null limit 1

and now I am not getting Nullpointer exception anymore and the test is passing but code coverage is still 0%,

do you know what is the issue now?
ravi soniravi soni
Hi Haseeb,
try following Test Class
@isTest
public class MqlServiceTest {
    public static testMethod void testSetMqlDate() {
        // retrieve business hours
       MqlService MS = new MqlService();
       
        List<sObject> lstSobject = new List<sObject>();
        Map<Id, sObject> oldRecordsById = new Map<Id, sObject> ();
        
        BusinessHours hours = [select id from BusinessHours where name = 'MQL Hours'];
        
        // account for contact
        Account account = new Account(name = 'test');
        insert account;

		// lead / contact to update as Open
		Lead updateLead = new Lead(lastName = 'test', company = 'test', email = 'test@test.com', status = 'Open - Not Contacted');
        Contact updateContact = new Contact(lastName = 'test', accountId = account.id ,lead_status__c = 'Open');
         lstSobject.add(updateLead);
        lstSobject.add(updateContact);
        insert lstSobject; 
        MS.setMqlDate(lstSobject,null);
        oldRecordsById.put(updateLead.Id, updateLead);
        oldRecordsById.put(updateContact.Id, updateContact);
        MS.setMqlDate(lstSobject,oldRecordsById);
         
        
    }
}

In my org this test class coverage is 92%. try this.
let me know, if it's helps you and marking best of this so that it could helps to others.
Thank You
This was selected as the best answer
Haseeb Ahmad 9Haseeb Ahmad 9
Hi Veer,

Happy New Year. 

Thank you so much, I got 95% coverage but I realized that this was been called from a lead trigger which I removed few calls go due to getting 

System.AsyncException: Rate Limiting Exception : AsyncApexExecutions Limit exceeded.

Now I am trying to fix that lead trigger and its trigger handler calls on my trigger I am getting 66% 

Trigger: 
 
trigger LeadTrigger on Lead (before insert, after insert, before update, after update) {
    try{
        TriggerHandler handler = new LeadTriggerHandler(Trigger.new, Trigger.newMap, Trigger.oldMap);
        
        if (Trigger.isBefore) {
            if (Trigger.isInsert) {
                handler.executeBeforeInsert();
            }
            
            if (Trigger.isUpdate) {
                handler.executeBeforeUpdate();            
            }
        }
        
        if (Trigger.isAfter) {
            if (Trigger.isInsert) {
                handler.executeAfterInsert();
            }
            
            if (Trigger.isUpdate) {
                handler.executeAfterUpdate();            
            }
        }
    }
    catch(Exception ex){
        DiagnosticService.DebugException(ex);
        DiagnosticService.PopAll();
    }
}

and on trigger handler 43% 
 
public class LeadTriggerHandler extends TriggerHandler {

    // services
    private final static MqlService mqlService = new MqlService();
    private static boolean isFirstTime = true;
     
    
    // new leads, new map, old map
    private final List<Lead> newLeads;
    private final Map<Id, Lead> newLeadsById;
    private final Map<Id, Lead> oldLeadsById;     
    
    // constructor
    public LeadTriggerHandler(List<sObject> newLeads, Map<Id, sObject> newLeadsById, Map<Id, sObject> oldLeadsById) {
        super();
        this.newLeads = (List<Lead>) newLeads;
        this.newLeadsById = (Map<Id, Lead>)newLeadsById;
        this.oldLeadsById = (Map<Id, Lead>)oldLeadsById;    
    }
    
    public override void executeBeforeInsert() {
        DiagnosticService.push('lead executeBeforeInsert');        

        mqlService.setMqlDate(newLeads, oldLeadsById);
            
        DiagnosticService.pop();                   
    }
    
    public override void executeAfterInsert() {     
        DiagnosticService.push('lead executeAfterInsert');              
        DiagnosticService.pop();
    }
    
    public override void executeBeforeUpdate() {
        DiagnosticService.push('lead executeBeforeUpdate');       
        mqlService.setMqlDate(newLeads, oldLeadsById);              
        
        DiagnosticService.pop();
    }
    
    public override void executeAfterUpdate() {
       if (isFirstTime){
            isFirstTime = false;
            DiagnosticService.push('lead executeAfterUpdate');                   
            DiagnosticService.pop();
       }

    }
    
    public override void executeBeforeDelete() {
        DiagnosticService.push('lead executeBeforeDelete');              

        DiagnosticService.pop();
    }
    
    public override void executeAfterDelete() {
        DiagnosticService.push('lead executeAfterDelete');              

        DiagnosticService.pop();
    }
    
    public override void executeAfterUndelete() {
        DiagnosticService.push('lead executeBeforeInsert');                      
        
        DiagnosticService.pop();
    }
}

I am trying to remove before/After delete and undelete lines from the trigger handler because those lines not getting any coverage or if you would help me to restore coverage for these 2 as well, that would be great, really appreciate your help on this, thank you.