I'm stumped because I cannot cover a simple for loop in my test class on the Opportunity object. Lines 15 through 28 are not being covered by my test class. I've inserted the Contracts, and yet the for loop is skipped. What has to be done for the for loop to receive coverage? Thank you

Trigger updateContracts on Opportunity (before update) {
    for(Opportunity o: Trigger.New){
        // Find Contracts associated to current Opportunity;
        // Order by Craeated Date to make most recent Contract show as first entry [0]
              Contract[] contracts = new List<Contract>();
              contracts = [SELECT Id, SBQQ__Opportunity__c, Contract_Status__c, Primary_Quote_Start_Date__c
                         FROM Contract
                         WHERE SBQQ__Opportunity__c =: o.Id AND Contract_Status__c != 'Expired' ORDER BY CreatedDate DESC];

            // Creating two distinct lists to prevent collision where same Contract enters expiration and resubmission (update)
              Contract[] expiredContracts = new List<Contract>();
              Contract[] updatedContracts = new List<Contract>();

              for(contract c : contracts){
                  // hold most recent contract in a variable  
                  Contract latestContract = contracts[0];

                  // check if it's the latest contract, skip over the latest contract
                  if(c <> latestContract && o.Submission_Counter__c > 0){
                       //c.Writable_End_Date__c = System.today();
                       c.Contract_Status__c  = 'Expired';
                  }if(o.Resubmit_Counter__c > 0 && o.Probability < 90){
                      // Resubmit to Checkout has been clicked indicating user wishes to update existing Contract 
                     c.StartDate = c.Primary_Quote_Start_Date__c;
                  }if(c == latestContract){
                     c.Resubmit_Resend_Email_Notice__c  = true; 

          // update existing Contract on Resubmit without creating new lines 
            if(o.Amendment_Opportunity__c == true){
                Contract[] amendedContract = new List<Contract>();
                amendedContract = [SELECT Id FROM Contract WHERE Id =: o.SBQQ__AmendedContract__c];
                Contract amendmentContract = amendedContract[0]; 
               // increment Amendment Counter on Contract to delete existing lines and CPQ package will create new lines 
               if(o.StageName == 'CPQ - Closed Won' && ((o.Submission_Counter__c > trigger.oldMap.get(o.Id).Submission_Counter__c) || o.Submission_Counter__c == 1)){
                  amendmentContract.Amendment_Counter__c = o.Amendment_Opp_Amendment_Counter__c; 
                  amendmentContract.Amendment_Text_For_Submission_eMail__c = o.Text_for_Email_Alert__c; 
                  update amendmentContract; 
               // If Amendment Opportunity is resubmitted, update checkbox that will trigger email update 
               if(o.StageName == 'CPQ - Closed Won' && ((o.Resubmit_Counter__c > trigger.oldMap.get(o.Id).Resubmit_Counter__c) || o.Resubmit_Counter__c == 1)){
                  amendmentContract.Amendment_Counter__c = o.Amendment_Opp_Amendment_Counter__c;
                  amendmentContract.Amendment_Text_For_Submission_eMail__c = o.Text_for_Email_Alert__c; 
                  update amendmentContract; 
              if(updatedContracts != null){
                  update updatedContracts;
              if(expiredContracts != null){
                  update expiredContracts;

The test class:

@isTest (SeeAllData = true) 
private class updateContractsTestClass {
    static testMethod void validateUpdateContracts() {
        Opportunity opp = new Opportunity(Submission_Counter__c = 1, 
                                      Resubmit_Counter__c = null, 
                                      StageName='CPQ - Closed Won', 
                                      CloseDate=Date.newInstance(1960, 2, 17)); 
        insert opp; 

        Opportunity opp2 = new Opportunity(Resubmit_Counter__c = 1, 
                                       Submission_Counter__c = 1, 
                                       StageName='CPQ - Closed Won', 
                                       CloseDate=Date.newInstance(1960, 2, 17)); 
        insert opp2;

        SBQQ__Quote__c quote = new SBQQ__Quote__c(SBQQ__Opportunity2__c = opp.id, 
                                       SBQQ__StartDate__c = Date.newInstance(2017, 3, 27), 
                                       SBQQ__Primary__c = true,
                                       SBQQ__SubscriptionTerm__c = 12);  
        insert quote; 

        SBQQ__Quote__c quote1 = new SBQQ__Quote__c(SBQQ__Opportunity2__c = opp2.id, 
                                       SBQQ__StartDate__c = Date.newInstance(2017, 2, 17), 
                                       SBQQ__Primary__c = true,
                                       SBQQ__SubscriptionTerm__c = 12);  
        insert quote1; 

        opp.SBQQ__PrimaryQuote__c = quote.id;      
        update opp; 

        opp2.SBQQ__PrimaryQuote__c = quote1.id; 
        update opp2; 

        opp = [SELECT id, SBQQ__PrimaryQuote__r.SBQQ__StartDate__c FROM Opportunity WHERE Name = 'ColorB'];
        update opp; 

        opp2 = [SELECT id FROM Opportunity WHERE Name = 'ColorC'];
        update opp2;

        // inserting Account to then reference it in the Contract (required by contract)
        Account testAccount = new Account(Name='testAccount'); 
        insert testAccount; 
        Id accountId = [SELECT Id FROM Account LIMIT 1].Id;   

        List<Contract> contractsList = new List<Contract>();     
        insert contractsList; 

        Contract firstContract = new Contract (AccountID = accountId, SBQQ__Opportunity__c = opp2.id,
                                           Contract_Status__c = 'Pending Checkout', Amendment_Counter__c = 1); 
        insert firstContract; 
        update firstContract; 

        Contract latestContract = new Contract(AccountID = accountId, SBQQ__Opportunity__c = opp.id, 
                                           Contract_Status__c = 'Pending Checkout'); 
        insert latestContract; 
        update latestContract;

        update contractslist; 

        // handle Amendment Opportunities 
        Opportunity amendmentOpp = new Opportunity(Submission_Counter__c = 1, 
                                      Resubmit_Counter__c = 1, 
                                      SBQQ__AmendedContract__c = firstContract.Id,
                                      StageName='1 - Qualification', 
                                      CloseDate=Date.newInstance(1960, 2, 17)); 
        insert amendmentOpp; 

        amendmentOpp.Resubmit_Counter__c = 2; 
        update amendmentOpp; 

        Opportunity closedAmendmentOpp = new Opportunity(Submission_Counter__c = 1, 
                                      Resubmit_Counter__c = 1, 
                                      SBQQ__AmendedContract__c = latestContract.Id,
                                      StageName='CPQ - Closed Won', 
                                      CloseDate=Date.newInstance(1960, 2, 17)); 
        insert closedAmendmentOpp; 

1 Answer 1


You trigger is fired whenever you update an opportunity and it queries all Contracts linked to these opportunities.

What you do in your test class is:

  1. Insert opportunities
  2. Update opportunities(probably to cover the trigger?)
  3. Insert contracts linked to these opportunities

The problem is that you trigger only fires at step 2., but at this moment there are no contracts, so the loop never executes. You should insert contracts prior to updating opportunities

Also, there are quite a few programming errors/mistakes/not-best-practices in your code.

I would recommend you keeping triggers logic-less and moving all your logic to a trigger handler. It also seems that you are working with multiple triggers per object(which is okay if each of the triggers only cover 1 event, but it's still advised to have 1 trigger with all events per object and a handler which handles the logic).

Also, you have queries and DML statements in a loop, which will most likely hit the governor limits quickly. Don't forget to bulkify your code.

And about your test class: make sure that each test method only tests 1 thing. You may need to write multiple test methods to actually test 1 method in your apex code. For example you have this method:

public static double divide(double d1, double d2) {
    return d1/d2;

For which you could write a simple test method:

@isTest static void testDivide() {
    myClass.divide(2.0, 1.0);

And it will give you coverage. However, you will not know if the method actually works, so you will need to assert it:

@isTest static void testDivide() {
    System.assertEquals(2.0, myClass.divide(2.0, 1.0), 'OMG! 2 divided by 1 is NOT 2!!!');

This will check that your method returns 2, if you want to divide 2 by 1. But is that enough? You have 100% coverage and an assert.

NO! You should also test some extreme values. For example division by zero. For this you will need to write another method:

@isTest static void testDivideByZero() {
    try {
        myClass.divide(2.0, 0.0);
        System.assert(false, 'code did not fail when dividing by zero');
    } catch (MathException e) {

And maybe also some tests with negative values? Depending how complex your logic is, you may need multiple test methods to test 1 functionality.

And, do not focus on getting coverage! Focus on creating all(as many as possible) real life scenarios and if a part of your code is not covered ask yourself a question:

Do I really need this piece of code?

For apex best practices you may want to see this page

  • Thank you @Novarg. Your comments were highly helpful. As a novice, I appreciate the tips. Jul 25, 2017 at 8:28
  • @COBETCKNN17 you are welcome. Glad I could help :)
    – Novarg
    Jul 25, 2017 at 12:04

You must log in to answer this question.

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