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
smohyee@ihrdc.comsmohyee@ihrdc.com 

Counting Active Contracts under Account. NullPointerException when testing.

I'm writing a simple trigger/class that, upon creation, editing or deletion of a Contract, counts all the 'Active' contracts underneath the related Account and updates the appropriate field on the Account record. 

 

I was up all last night trying to figure out the test failure errors I was getting, to no avail. I have a feeling somone on here will recognize the problem within 30 seconds =). By the way, I'm a newbie trying to learn, so any comments as to style, form and functionality are appreciated!

 

The error message is: 

 

Error Message	System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, Contract_Triggers: execution of AfterInsert

caused by: System.NullPointerException: Attempt to de-reference a null object

Class.Contract_Handler.Count_Active_Contracts: line 14, column 1
Trigger.Contract_Triggers: line 11, column 1: []
Stack Trace	
Class.Contract_Test_Data.createAccountWithContracts: line 16, column 1 Class.Contract_Test_Methods.testContractCount_case1: line 9, column 1

 

I believe the problem lies somewhere between the actual Class and the Test Class responsible for creating test data. 

 

Here is the current form of the Contract_Handler class:

 

public class Contract_Handler{

    public static Integer Count_Active_Contracts(Account acc)

/*This method is passed an Account object, counts the related ACTIVE contracts(ie, in between start and end dates), and updates the Active_Contracts__c field on the account record. Count is also returned for other potential uses.*/ Integer count = 0; Date today = Date.today(); //ATTENTION: I Believe the issue has to do with the following for loop, due to acc.Contracts not having any //members (I tried a SOQL For Loop first and got the same error). for (Contract c : acc.Contracts){ if(today >= c.StartDate && today <= c.EndDate ){ count++; } } acc.Active_Contracts__c = count; update acc; return count; } }

 

Here is the test data I'm generating. ONLY TEST CASE == 1 is being tested right now, so I've omitted the code for the other cases:

 

@isTest
public class Contract_Test_Data{

    public static Account createAccountWithContracts(Integer testCase){
        /*3 test cases, based on types of contracts associated with account:
			1)Two Contracts, neither are active, one is expired, one is upcoming 
			2)Three Contracts, one active, one expired, one upcoming 
			3)Two Contracts, both active
		*/
        
        Account testAccount = createAccountRecord();
 
        
        if(testCase == 1){

            insert new Contract[]{new Contract(AccountId = testAccount.Id,
                                               Account = testAccount,
                			       StartDate = date.parse('01/01/2009'),
                                               ContractTerm = 12), 
                     	         new Contract(AccountId = testAccount.Id, 
                                               Account = testAccount,
                			       StartDate = date.parse('10/01/2014'),
                                               ContractTerm = 12)};
            
        }

        return testAccount;        	
    }
    
    
    private static Account createAccountRecord(){
        Account acc = new Account(Name = 'Test');
        
        insert acc;
        return acc;        
    }

    
}

 

 

My test trigger calls Contract_Test_Data.createAccountWithContracts(1), which causes the creation of the test Account record, along with two Contract records which will both qualify as INACTIVE. Inserting these contract records triggers a for loop through those contracts, with the method Contract_Handler.Count_Active_Contracts being passed their parent account.

 

 

An aside on Checkpoints:  Debuggin in the Developer Console seems to be flaky and a pain. I tried setting checkpoints in my various triggers/classes, and only got results a few times when running the tests. Also, I tried to add an action script to the checkpoints to run APEX code (a system.debug call on some variables), but I have no idea where those logs would appear. Documentation seems scarce, can anyone point me to anything other than the one paragraph on the Developer Console help page? 

 

 

Thank you all for reading, and for your help!

Best Answer chosen by Admin (Salesforce Developers) 
Avidev9Avidev9

Sorry my mistake

 

Modify the code like this

 

and pass Contract.AccountId to the class

 

public class Contract_Handler{

    public static Integer Count_Active_Contracts(Id accId)

/*This method is passed an Account object, counts the related ACTIVE contracts(ie, in between start and end dates), and updates the Active_Contracts__c field on the account record. Count is also returned for other potential uses.*/
        
        
        Integer count =[SELECT Count() FROM Contract WHERE AccountId=:accId AND StartDate >= TODAY AND TODAY<= EndDate ];
       // Date today = Date.today(); 
        
//ATTENTION: I Believe the issue has to do with the following for loop, due to acc.Contracts not having any //members (I tried a SOQL For Loop first and got the same error). 
         /*                       
        for (Contract c : [SELECT Id FROM Contract]){
        	if(today >= c.StartDate && today <= c.EndDate ){
               	count++;
            }
         }   
        */
        acc.Active_Contracts__c = count;
        update acc;
        
        return count;
    }    
}

All Answers

Avidev9Avidev9
Well how are you exactly calling "Contract_Handler" ?
smohyee@ihrdc.comsmohyee@ihrdc.com

Thanks for replying Avidev9. Here is the actual trigger: 

 

trigger Contract_Triggers on Contract (after insert, after update, after delete) {

    //Iterates through affected contracts and passes Account ID to 'Count_Active_Contracts' method
    if(trigger.isDelete){
        for(Contract c: trigger.old){
            Contract_Handler.Count_Active_Contracts(c.Account);
        }
    }
    else{
        for(Contract c: trigger.new){
    		Contract_Handler.Count_Active_Contracts(c.Account);    //***THIS IS LINE 11***
        }
    }
    
}

 

The error message traces to line 11, which I've noted as a comment in the code.

Avidev9Avidev9

Well there are some basic problem with trigger.

  • Trigger doesn pull all the related records data automatically, If you have a trigger on contract it will have fields of contract only populated
  • Below code changes should fix it but its not bulk safe

 

public class Contract_Handler{

    public static Integer Count_Active_Contracts(Account acc)

/*This method is passed an Account object, counts the related ACTIVE contracts(ie, in between start and end dates), and updates the Active_Contracts__c field on the account record. Count is also returned for other potential uses.*/
        
        
        Integer count =[SELECT Count() FROM Contract WHERE AccountId=:acc.Id AND StartDate >= TODAY AND TODAY<= EndDate ];
       // Date today = Date.today(); 
        
//ATTENTION: I Believe the issue has to do with the following for loop, due to acc.Contracts not having any //members (I tried a SOQL For Loop first and got the same error). 
         /*                       
        for (Contract c : [SELECT Id FROM Contract]){
        	if(today >= c.StartDate && today <= c.EndDate ){
               	count++;
            }
         }   
        */
        acc.Active_Contracts__c = count;
        update acc;
        
        return count;
    }    
}

 

smohyee@ihrdc.comsmohyee@ihrdc.com

I appreciate the quick response! I will try out your code now. Let me ask a few questions to help me understand how this works:

 

1.  Your first point about triggers not pulling all related data... does that mean that my passing the Account object related to the contract won't work in the trigger code? What if I passed c.AccountID instead?

 

for(Contract c: trigger.new){
    Contract_Handler.Count_Active_Contracts(c.Account);
    }

 

2. On the Count_Active_Contracts code, I notice you replaced

 

        for (Contract c : acc.Contracts){
        	if(today >= c.StartDate && today <= c.EndDate ){
               	count++;
            }
         }

 

with a nifty SOQL function that handles the counting for me. Thanks for that suggestion!

 

But why is it my attempt to access a list of child objects (ie 'acc.Contracts') produces the error?

 

Also, I had originally used a SOQL for loop  instead:

for (Contract c : [SELECT Id, StartDate, EndDate FROM Contract Where AccountID = :acc.id]){
}

 

Based on this documentation, I thought it would work, but I got a similar error. Do you know why this failed?

 

 

Again, thank you very much for your time and help!

smohyee@ihrdc.comsmohyee@ihrdc.com

Avidev9,

 

Unfortunately, your code gave me the same NullPointerException error! This time it is traced to the new SOQL aggregate query you wrote. I'm trying to use checkpoints to see if acc.id actually has a value when the query runs. Any ideas?

Avidev9Avidev9

Sorry my mistake

 

Modify the code like this

 

and pass Contract.AccountId to the class

 

public class Contract_Handler{

    public static Integer Count_Active_Contracts(Id accId)

/*This method is passed an Account object, counts the related ACTIVE contracts(ie, in between start and end dates), and updates the Active_Contracts__c field on the account record. Count is also returned for other potential uses.*/
        
        
        Integer count =[SELECT Count() FROM Contract WHERE AccountId=:accId AND StartDate >= TODAY AND TODAY<= EndDate ];
       // Date today = Date.today(); 
        
//ATTENTION: I Believe the issue has to do with the following for loop, due to acc.Contracts not having any //members (I tried a SOQL For Loop first and got the same error). 
         /*                       
        for (Contract c : [SELECT Id FROM Contract]){
        	if(today >= c.StartDate && today <= c.EndDate ){
               	count++;
            }
         }   
        */
        acc.Active_Contracts__c = count;
        update acc;
        
        return count;
    }    
}
This was selected as the best answer
Avidev9Avidev9
Related to the other questions!
In a trigger you can access the values of the immediate fields not anything from the relationship. You can access "AccountId" you can't access the full Account and all it fields.

To access the fields of the related object you need to do a explicit query with all the required fields
smohyee@ihrdc.comsmohyee@ihrdc.com

Success! Thank you for your help Avidev9.

 

 

 

Here are my takeaways for anyone else who stumbles on this:

 

  • If I need related object data in my program, I need to query it specifically, as it isn't brought into the memory space along with the triggered object. 

 

  • Passing actual object records between methods  is troublesome, though I'm not exactly clear why. But since you can always pass an Id then query the desired object record in the called method, it's easy enough to get around.

for example, originally my Test method that created the Data returned an Account object, so my Test Trigger looked like this:

 

static testmethod void testContractCount_case1(){
//Expected result = 0
    
        test.startTest();
        Account testAcct = Contract_Test_Data.createAccountWithContracts(1);
        test.stopTest();
        
        System.assertEquals(0, testAcct.Active_Contracts__c);       
    }

 

This didn't work, however, as testAcct.Active_Contracts had a NULL value instead of 0. But by passing Ids, instead, it worked! Here's my updated Test trigger code:

 

@isTest
public class Contract_Test_Methods{

    //The next three methods tests different scenarios for the same code in Contract_Handler.Count_Active_Contracts  
    static testmethod void testContractCount_case1(){
        //Expected result = 0
    
        test.startTest();
        Id testAcctId = Contract_Test_Data.createAccountWithContracts(1);
        test.stopTest();
        
        System.assertEquals(0, [SELECT Active_Contracts__c FROM Account WHERE Id =: testAcctId].Active_Contracts__c);       
    }
        
    
}

 

I also changed my createAccountWithContracts method to retun the Id of the account it created, not the account itself. 

 

 

 

What I'm not clear on is the issue with passing objects, which I assume is in reality passing a reference to an object record. Clearly there are limitations, and I need to find the documentation on when it's appropriate to pass an entire record, if at all. 

Avidev9Avidev9

Let me help you out with passing an object as argument.

 

  • Well only problem with your approach, trigger gets access to only immediate fields not the related objects.
  • If you want to pass the whole object you need to query the fields of the related object you can always pass the contract object as it was always in context. But since you passed in contract.Account it was coming null because trigger never cached that information.

Ssomething like this would have worked

 

Contract con = [SELECT Id Contract, Account.Id, Account.Name FROM Contract WHERE Id =: trigger.new[0]];

 

Account acc = con.Account;

Now you can access 

  • Id
  • Name

As you explicity queried them

 

smohyee@ihrdc.comsmohyee@ihrdc.com
This makes sense. I'll do a variation of my code trying the object pass again, and this time I'll make sure to Query all the account fields I need.

You're like a ray of sunshine through the Salesforce Cloud, Avidev9!