Apex – Null Reference on Apex Specialist Superbadge Challenge 4

Been spinning my wheels on this for awhile. Frustrating thing about these Trailheads is no point of reference for what tests are failing on Salesforce's end that might be producing this error.

Here is the code in question.

public class MaintenanceRequestHelper {

public static void updateWorkOrders(List<Case> updatedCases, Map<Id, Case> oldMap, Map<Id, Case> newMap) {
    Map<Id, Case> validCases = new Map<Id, Case>();
    Map<Id, Decimal> caseToShortestCycle = new Map<Id, Decimal>();
    List<Work_Part__c> newWorkParts = new List<Work_Part__c>();
    Map<Id, Case> casesToInsert = new Map<Id, Case>();


    for(Case c : updatedCases) {
        if(oldMap.get(c.Id).Status != 'Closed' && newMap.get(c.Id).Status == 'Closed' && c.Equipment__c != null && (c.Type == 'Repair' || c.Type == 'Routine Maintenance')) {
            validCases.put(c.Id, c);
        }
    }

    List<Work_Part__c> validWorkParts = [SELECT Maintenance_Request__c, Equipment__r.Maintenance_Cycle__c 
                                       FROM Work_Part__c 
                                       WHERE Maintenance_Request__c =: validCases.keyset() WITH SECURITY_ENFORCED];

    for(Work_Part__c wp : validWorkParts) {
        if (wp.Equipment__r.Maintenance_Cycle__c != null && wp.Maintenance_Request__c != null) {
            if(caseToShortestCycle.containsKey(wp.Maintenance_Request__c) && wp.Equipment__r.Maintenance_Cycle__c < caseToShortestCycle.get(wp.Maintenance_Request__c)) {
                caseToShortestCycle.put(wp.Maintenance_Request__c, wp.Equipment__r.Maintenance_Cycle__c);
            } else {
                caseToShortestCycle.put(wp.Maintenance_Request__c, wp.Equipment__r.Maintenance_Cycle__c);
            }
        }
    }

    for(Case c : validCases.values()) {
        casesToInsert.put(c.Id, new Case(Vehicle__c = c.Vehicle__c, 
                                        Equipment__c = c.Equipment__c,
                                        Type = 'Routine Maintenance',
                                        Date_Reported__c = Date.today(),
                                        Date_Due__c = Date.today().addDays(caseToShortestCycle.get(c.Id).intValue()),
                                        Status = 'New',
                                        Product__c = c.Product__c,
                                        ContactId = c.ContactId,
                                        AccountId = c.AccountId,
                                        Origin = c.Origin,
                                        AssetId = c.AssetId,
                                        Reason = c.Reason,
                                        ParentId = c.Id,
                                        Subject = 'New thing a majig'));

    }

    if(casesToInsert.size() > 0) {

        insert casesToInsert.values();

    }

    for(Work_Part__c wp : validWorkParts) {
        if(casesToInsert.containsKey(wp.Maintenance_Request__c)) {
            newWorkParts.add(new Work_Part__c(Maintenance_Request__c = casesToInsert.get(wp.Maintenance_Request__c).Id));
        }
    }
}           

}

Here is the TestDataFactory code incase this is needed.

public class TestDataFactory {
public static List<Case> buildMaintenanceRequest() {
    List<Case> cases = new List<Case>();
    List<Work_Part__c> wp = new List<Work_Part__c>();
    Vehicle__c testVehicle = new Vehicle__c(Name = 'testvehicle');

    insert testVehicle;

    Product2 equip1 = new Product2(Name = 'Cooling Fan', 
                                    Maintenance_Cycle__c = 3, 
                                    Replacement_Part__c = true, 
                                    Cost__c = 100,
                                    Lifespan_Months__c = 24,
                                    Current_Inventory__c = 100,
                                    Warehouse_SKU__c = '100000');
    Product2 equip2 = new Product2(Name = 'Cooling Fan', 
                                    Maintenance_Cycle__c = 8, 
                                    Replacement_Part__c = true, 
                                    Cost__c = 100,
                                    Lifespan_Months__c = 24,
                                    Current_Inventory__c = 100,
                                    Warehouse_SKU__c = '100000');

    insert equip1;
    insert equip2;

    for(Integer i=0; i<300; i++) {
        cases.add(new Case(
            Status = 'New',
            Vehicle__c = testVehicle.Id, 
            Equipment__c = equip1.Id, 
            Type = 'Routine Maintenance', 
            Date_Reported__c = Date.today(), 
            Subject = 'New thing a majig',
            SuppliedName = 'Test')
        );
    }

    for(Integer i=0; i<100; i++) {
        cases.add(new Case(
            Status = 'New',
            Vehicle__c = testVehicle.Id, 
            Equipment__c = equip1.Id, 
            Type = 'Structural', 
            Date_Reported__c = Date.today(), 
            Subject = 'New thing a majig',
            SuppliedName = 'Test')
        );
    }

    insert cases;

    for(Case c : cases) {
        wp.add(new Work_Part__c(Maintenance_Request__c = c.Id, Equipment__c = equip1.id));
        wp.add(new Work_Part__c(Maintenance_Request__c = c.Id, Equipment__c = equip2.id));
    }

    insert wp;

    return cases;
}

}

Challenge Not yet complete… here's what's wrong:
There was an unexpected error in your org which is preventing this assessment check from completing: System.DmlException: Update failed. First exception on row 0 with id 5003h000002xOppAAE; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, MaintenanceRequest: execution of AfterUpdate caused by: System.NullPointerException: Attempt to de-reference a null object Class.MaintenanceRequestHelper.updateWorkOrders: line 35, column 1 Trigger.MaintenanceRequest: line 9, column 1: []

I'm not looking for the answer necessarily, just how to best approach determining the root cause of this issue. If I could just get some feedback on how to go about tracing the problem, that would be far more valuable to me.

I did manage to find an old article addressing this but have been unsuccessful in my attempts to use it as a guide to solving the problem.

https://developer.salesforce.com/forums/?id=906F0000000kGqUIAU

Best Answer

NullPointerException in your own code is always something you can trace out for yourself, and it's virtually always a logic fault - not an issue with Salesforce itself.

Line 35 appears to be this:

Date_Due__c = Date.today().addDays(caseToShortestCycle.get(c.Id).intValue()),

and sure enough, that's a top suspect for a NullPointerException. get() returns null if the given key is not present.

Failing to check whether the return value from get() is null before dereferencing it, or failing to check that the map contains the given key before requesting it, is a very common culprit for NullPointerException.

The first thing to look at is "what's the potential disjunct between caseToShortestCycle and validCases?" - why could they have different contents or not be congruent?

That may reveal a logical fault elsewhere in the code, or it may reveal that you need to write more logic here to handle a specific code path where the two collections do not line up.