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
Ember Campbell 10Ember Campbell 10 

Help with Code coverage at code coverage is 0%.

Hi all,

I'm new to apex triggers and had posted in the community to get a trigger to create a field that summarizes Opportunities Product Families.  I was so excited when a wondeful community member was able to help. The code works create in my sandbox.  I attempted to do a change set to move into production but have been unable.  I submitted a case to Salesforce support and they recommded to post this forum since we are not on a Premier Sucess plan.

Code:
trigger populateProductFamily  on OpportunityLineItem (after Insert,before delete) {


if((trigger.IsAfter  && trigger.IsInsert)  || (trigger.IsBefore  && trigger.IsDelete)  )      // run after insert and before delete


{
//map to hold product family for a gven oppty
Map<id,Set<String>> oppprodFamilyMap = new Map<id,Set<String>>();
//get all oppty id
List<id> oppId =  new List<Id>();
List<opportunitylineitem> triggerList = new List<opportunitylineitem>();
if(trigger.IsDelete)
{  system.debug('in delete');
triggerList = trigger.old;
}

else
{
triggerList = trigger.new;
}


for (opportunitylineitem ol : triggerList )  {

   oppId.add(ol.opportunityid);

}

// query all opportunity for opportunitylineitem inserted



List<opportunity> oppList = [select id ,Opportunity_Product_Family__c from opportunity where id in :oppId];




// query all opportunitylineitem linked with opportunity of current opportunitylineitem
//we need this so that we can get all product family for a a given oppty

List<opportunitylineitem> olList  =  new List<opportunitylineitem>();

if(trigger.IsDelete) {
olList = [select id,opportunityid,product2.family  from opportunitylineitem where opportunityid in :oppList  and id not in  :triggerList  ];

}

else

{
olList = [select id,opportunityid,product2.family  from opportunitylineitem where opportunityid in :oppList  ];

}

system.debug('---ollist' + olList  );


//now we need to separate productfamily by opportunity


for (opportunitylineitem ol : olList )  {

IF(ol.product2.family  != null)  {
if(oppprodFamilyMap.get(ol.opportunityid) != null)  {

Set<String>  pFamilySet   = oppprodFamilyMap.get(ol.opportunityid);
pFamilySet.add(ol.product2.family);
oppprodFamilyMap.put(ol.opportunityid,pFamilySet);

}

else {
 Set<String> pFamilySet = new Set<String> ();
 pFamilySet.add(ol.product2.family);
oppprodFamilyMap.put(ol.opportunityid,pFamilySet);


}

}



}



// now we have got all productfamily for a given oppty.
//we need to concat  and update field


  //opplist to update
  
  List<opportunity> oppListToUpdate = new List<opportunity>();


for(opportunity  o : oppList)  



List<String>  pFamilyList = new List<String>(oppprodFamilyMap.get(o.Id));
String pFamily  = string.join(pFamilyList ,',');

o.Opportunity_Product_Family__c  = pFamily;
oppListToUpdate.add(o);

 


}


if(oppListToUpdate.size()>0)

update  oppListToUpdate;







}      

}

Error - Code Coverage Failure
Your code coverage is 0%. You need at least 75% coverage to complete this deployment.
populateProductFamily

I'm sure I have to change the sandbox, then the change set and try to deploy again into but everything I've tried fails.  Any help would be greatly appreciated.  
WEN JIEWEN JIE
Hi Ember,

You need to write an apex test class.
For your trigger, you could insert an OpportunityLineItem record. Then your trigger will run ("after insert" scenario) .
And you could also delete this record which you created before in your test class, then "before delete" scenario will be cover.

Thanks.
Ember Campbell 10Ember Campbell 10
hi @wen jen,  can you give me more of an example that I could follow in the sandbox and then add to the change set?  
Ankit Kalsara 6Ankit Kalsara 6
Hi Ember,

Can you try running the below test class and see if the code coverage goes above 75%?
 
@isTest public class populateProductFamilyTest{

    //method 1 - create OLI's
    private static testMethod void createOlis(){
          // create account
          Account act = new Account();
          act.Name = 'test account';
          INSERT act;

          //create Opportunity
          Opportunity opp = new Opportunity();
          opp.Name = 'test Opp';
          opp.AccountId = act.Id;
          opp.Type = 'System';
          opp.RecordTypeId                   = [SELECT id 
                                          FROM RecordType 
                                         WHERE SObjectType='Opportunity' 
                                           AND DeveloperName=:type].id; 
         opp.StageName                      = 'Prospecting';
         opp.CloseDate                      = Date.today();
         INSERT opp;

         // create product
         Product2 prod1 		     = new Product2();
         prod1.Name                = 'test product 1';
         prod1.Family            = 'Systems'
         prod1.IsActive 		     = true;        
        INSERT prod1;

        // create price book entry for this product
        PriceBookEntry pbe = new PriceBookEntry(PriceBook2Id = Test.getStandardPricebookId(), 
                                                   Product2Id = prod1.Id,   
                                                   UnitPrice=100, 
                                                   IsActive=true);
         
        // create product
        Product2 prod2 		     = new Product2();
        prod2.Name                = 'test product 2';
        prod2.Family            = 'Device'
        prod2.IsActive 		     = true;        
       INSERT prod2;

       // create price book entry for this product
       PriceBookEntry pbe1 = new PriceBookEntry(PriceBook2Id = Test.getStandardPricebookId(), 
                                                  Product2Id = prod2.Id,   
                                                  UnitPrice=100, 
                                                  IsActive=true);

        // Create 1st OLI with licensed product --> this will trigger after insert trigger
		    OpportunityLineItem oli1 = new OpportunityLineItem();
		    oli1.OpportunityId       = opp.Id;
        oli1.Quantity            = 2;        
        oli1.PriceBookEntryId    = pbe1.Id;
        oli1.UnitPrice           = pbe1.UnitPrice;    
		    INSERT oli1; 

        // Create 2nd OLI with licensed product --> this will trigger after insert trigger
		    OpportunityLineItem oli2 = new OpportunityLineItem();
		    oli2.OpportunityId       = opp.Id;
        oli2.Quantity            = 2;        
        oli2.PriceBookEntryId    = pbe2.Id;
        oli2.UnitPrice           = pbe2.UnitPrice;    
		    INSERT oli2; 

    }

    // method 2 - delete OLI's
        private static testMethod void deleteOlis(){

        // create account
        Account act = new Account();
        act.Name = 'test account';
        INSERT act;

        //create Opportunity
        Opportunity opp = new Opportunity();
        opp.Name = 'test Opp';
        opp.AccountId = act.Id;
        opp.Type = 'System';
        opp.RecordTypeId                   = [SELECT id 
                                        FROM RecordType 
                                       WHERE SObjectType='Opportunity' 
                                         AND DeveloperName=:type].id; 
       opp.StageName                      = 'Prospecting';
       opp.CloseDate                      = Date.today();
       INSERT opp;

       // create product
       Product2 prod1 		     = new Product2();
       prod1.Name                = 'test product 1';
       prod1.Family            = 'Systems'
       prod1.IsActive 		     = true;        
      INSERT prod1;

      // create price book entry for this product
      PriceBookEntry pbe = new PriceBookEntry(PriceBook2Id = Test.getStandardPricebookId(), 
                                                 Product2Id = prod1.Id,   
                                                 UnitPrice=100, 
                                                 IsActive=true);
       
      // create product
      Product2 prod2 		     = new Product2();
      prod2.Name                = 'test product 2';
      prod2.Family            = 'Device'
      prod2.IsActive 		     = true;        
     INSERT prod2;

     // create price book entry for this product
     PriceBookEntry pbe1 = new PriceBookEntry(PriceBook2Id = Test.getStandardPricebookId(), 
                                                Product2Id = prod2.Id,   
                                                UnitPrice=100, 
                                                IsActive=true);

      // Create 1st OLI with licensed product --> this will trigger after insert trigger
      OpportunityLineItem oli1 = new OpportunityLineItem();
      oli1.OpportunityId       = opp.Id;
      oli1.Quantity            = 2;        
      oli1.PriceBookEntryId    = pbe1.Id;
      oli1.UnitPrice           = pbe1.UnitPrice;    
      INSERT oli1; 

      // Create 2nd OLI with licensed product --> this will trigger after insert trigger
      OpportunityLineItem oli2 = new OpportunityLineItem();
      oli2.OpportunityId       = opp.Id;
      oli2.Quantity            = 2;        
      oli2.PriceBookEntryId    = pbe2.Id;
      oli2.UnitPrice           = pbe2.UnitPrice;    
      INSERT oli2; 

      // now we need to run before delete trigger, thus delete the Olis now
      DELETE oli1;
      DELETE oli2;    
    }

}

 
Ember Campbell 10Ember Campbell 10
Hello Ankit,

I think we are pretty close.  When I added the class, there were 10 errors.  I was able to fix four with adding a missing ";" to each row.  Now I'm struggling with the variables.  Seems like the code is not creating them to find them.  I'll include a screenshot of the six errors and keep looking for a solution. 
Variable errors
Thank you for the help.
Ankit Kalsara 6Ankit Kalsara 6
Hi Ember,

I have corrected the code. Can you try below code.
 
@isTest public class populateProductFamilyTest{

    //method 1 - create OLI's
    private static testMethod void createOlis(){
          // create account
          Account act = new Account();
          act.Name = 'test account';
          INSERT act;

          //create Opportunity
          Opportunity opp = new Opportunity();
          opp.Name = 'test Opp';
          opp.AccountId = act.Id;
          opp.Type = 'System';
          opp.RecordTypeId                   = [SELECT id 
                                          FROM RecordType 
                                         WHERE SObjectType='Opportunity' 
                                           AND DeveloperName=:opp.type].id; 
         opp.StageName                      = 'Prospecting';
         opp.CloseDate                      = Date.today();
         INSERT opp;

         // create product
         Product2 prod1 		     = new Product2();
         prod1.Name              = 'test product 1';
         prod1.Family            = 'Systems'
         prod1.IsActive 		     = true;        
         INSERT prod1;

        // create price book entry for this product
        PriceBookEntry pbe1 = new PriceBookEntry(PriceBook2Id = Test.getStandardPricebookId(), 
                                                   Product2Id = prod1.Id,   
                                                   UnitPrice=100, 
                                                   IsActive=true);
        INSERT pbe1;                                           
         
        // create product
        Product2 prod2 		     = new Product2();
        prod2.Name             = 'test product 2';
        prod2.Family           = 'Device'
        prod2.IsActive 	 	     = true;        
        INSERT prod2;

       // create price book entry for this product
       PriceBookEntry pbe2 = new PriceBookEntry(PriceBook2Id = Test.getStandardPricebookId(), 
                                                  Product2Id = prod2.Id,   
                                                  UnitPrice=100, 
                                                  IsActive=true);

        INSERT pbe2;

        // Create 1st OLI with licensed product --> this will trigger after insert trigger
		    OpportunityLineItem oli1 = new OpportunityLineItem();
		    oli1.OpportunityId       = opp.Id;
        oli1.Quantity            = 2;        
        oli1.PriceBookEntryId    = pbe1.Id;
        oli1.UnitPrice           = pbe1.UnitPrice;    
		    INSERT oli1; 

        // Create 2nd OLI with licensed product --> this will trigger after insert trigger
		    OpportunityLineItem oli2 = new OpportunityLineItem();
		    oli2.OpportunityId       = opp.Id;
        oli2.Quantity            = 2;        
        oli2.PriceBookEntryId    = pbe2.Id;
        oli2.UnitPrice           = pbe2.UnitPrice;    
		    INSERT oli2; 

    }

    // method 2 - delete OLI's
        private static testMethod void deleteOlis(){

        // create account
        Account act = new Account();
        act.Name = 'test account';
        INSERT act;

        //create Opportunity
        Opportunity opp = new Opportunity();
        opp.Name = 'test Opp';
        opp.AccountId = act.Id;
        opp.Type = 'System';
        opp.RecordTypeId                   = [SELECT id 
                                        FROM RecordType 
                                       WHERE SObjectType='Opportunity' 
                                         AND DeveloperName=:opp.type].id; 
       opp.StageName                      = 'Prospecting';
       opp.CloseDate                      = Date.today();
       INSERT opp;

       // create product
       Product2 prod1 		     = new Product2();
       prod1.Name              = 'test product 1';
       prod1.Family            = 'Systems'
       prod1.IsActive 		     = true;        
       INSERT prod1;

      // create price book entry for this product
      PriceBookEntry pbe1 = new PriceBookEntry(PriceBook2Id = Test.getStandardPricebookId(), 
                                                 Product2Id = prod1.Id,   
                                                 UnitPrice=100, 
                                                 IsActive=true);
      INSERT pbe1;                                           
       
      // create product
      Product2 prod2 		     = new Product2();
      prod2.Name             = 'test product 2';
      prod2.Family           = 'Device'
      prod2.IsActive 		     = true;        
      INSERT prod2;

     // create price book entry for this product
     PriceBookEntry pbe2 = new PriceBookEntry(PriceBook2Id = Test.getStandardPricebookId(), 
                                                Product2Id = prod2.Id,   
                                                UnitPrice=100, 
                                                IsActive=true);
      INSERT pbe2;                                          

      // Create 1st OLI with licensed product --> this will trigger after insert trigger
      OpportunityLineItem oli1 = new OpportunityLineItem();
      oli1.OpportunityId       = opp.Id;
      oli1.Quantity            = 2;        
      oli1.PriceBookEntryId    = pbe1.Id;
      oli1.UnitPrice           = pbe1.UnitPrice;    
      INSERT oli1; 

      // Create 2nd OLI with licensed product --> this will trigger after insert trigger
      OpportunityLineItem oli2 = new OpportunityLineItem();
      oli2.OpportunityId       = opp.Id;
      oli2.Quantity            = 2;        
      oli2.PriceBookEntryId    = pbe2.Id;
      oli2.UnitPrice           = pbe2.UnitPrice;    
      INSERT oli2; 

      // now we need to run before delete trigger, thus delete the Olis now
      DELETE oli1;
      DELETE oli2;    
    }

}

 
Ember Campbell 10Ember Campbell 10
Hi Ankit,

Looks like we were able to get the class to not show errors in the developer console but the class still has zero coverage.  I'm not sure what I'm doing wrong so I'll include two screenshots for reference:

Both fail:
User-added image

Detailed message on failure
User-added image
Ankit Kalsara 6Ankit Kalsara 6
Hi Ember,

The error says list has no rows for assignement to sobject. System is not getting the data from SELECT query. Can you put system.debug() after the SELECT query and see if you are getting any data.

User-added image

I am getting 100% code coverage.

Sharing trigger code and class for reference.

Trigger: populateProductFamily
trigger populateProductFamily  on OpportunityLineItem (after Insert,before delete) {
  
  
  if((trigger.IsAfter  && trigger.IsInsert)  || (trigger.IsBefore  && trigger.IsDelete)  )      // run after insert and before delete
  
  
  {
    //map to hold product family for a gven oppty
    Map<id,Set<String>> oppprodFamilyMap = new Map<id,Set<String>>();
    //get all oppty id
    List<id> oppId =  new List<Id>();
    List<opportunitylineitem> triggerList = new List<opportunitylineitem>();
    if(trigger.IsDelete)
    {  system.debug('in delete');
    triggerList = trigger.old;
  }
  
  else
  {
    triggerList = trigger.new;
  }
  
  
  for (opportunitylineitem ol : triggerList )  {
    
    oppId.add(ol.opportunityid);
    
  }
  
  // query all opportunity for opportunitylineitem inserted
  
  
  
  List<opportunity> oppList = [select id ,Opportunity_Product_Family__c from opportunity where id in :oppId];
  
  
  
  
  // query all opportunitylineitem linked with opportunity of current opportunitylineitem
  //we need this so that we can get all product family for a a given oppty
  
  List<opportunitylineitem> olList  =  new List<opportunitylineitem>();
  
  if(trigger.IsDelete) {
    olList = [select id,opportunityid,product2.family  from opportunitylineitem where opportunityid in :oppList  and id not in  :triggerList  ];
    
  }
  
  else
  
  {
    olList = [select id,opportunityid,product2.family  from opportunitylineitem where opportunityid in :oppList  ];
    
  }
  
  system.debug('---ollist' + olList  );
  
  
  //now we need to separate productfamily by opportunity
  
  
  for (opportunitylineitem ol : olList )  {
    
    IF(ol.product2.family  != null)  {
      if(oppprodFamilyMap.get(ol.opportunityid) != null)  {
        
        Set<String>  pFamilySet   = oppprodFamilyMap.get(ol.opportunityid);
        pFamilySet.add(ol.product2.family);
        oppprodFamilyMap.put(ol.opportunityid,pFamilySet);
        
      }
      
      else {
        Set<String> pFamilySet = new Set<String> ();
        pFamilySet.add(ol.product2.family);
        oppprodFamilyMap.put(ol.opportunityid,pFamilySet);
        
        
      }
      
    }
    
    
    
  }
  
  
  
  // now we have got all productfamily for a given oppty.
  //we need to concat  and update field
  
  
  //opplist to update
  
  List<opportunity> oppListToUpdate = new List<opportunity>();
  
  
  for(opportunity  o : oppList)
  {
    
    
    List<String>  pFamilyList = new List<String>(oppprodFamilyMap.get(o.Id));
    String pFamily  = string.join(pFamilyList ,',');
    
    o.Opportunity_Product_Family__c  = pFamily;
    oppListToUpdate.add(o);
    
    
    
    
  }
  
  
  if(oppListToUpdate.size()>0)
  
  update  oppListToUpdate;
  
  
  
  
  
  
  
}

}

Class: populateProductFamilyTest
@isTest public class populateProductFamilyTest{

    //method 1 - create OLI's
    private static testMethod void createOlis(){
          // create account
          Account act = new Account();
          act.Name = 'test account';
          INSERT act;

          //create Opportunity
          Opportunity opp 	= new Opportunity();
          opp.Name 			= 'test Opp';
          opp.AccountId 	= act.Id;
          opp.Type 			= 'System';
          opp.RecordTypeId  = [SELECT id 
                                 FROM RecordType 
                                WHERE SObjectType='Opportunity' 
                                  AND DeveloperName=:opp.type].id; 
          opp.StageName     = 'Prospecting';
          opp.CloseDate     = Date.today();
          INSERT opp;

          // create product
          Product2 prod1 	= new Product2();
          prod1.Name        = 'test product 1';
          prod1.Family      = 'Systems';
          prod1.IsActive 	= true;        
          INSERT prod1;

        // create price book entry for this product
        PriceBookEntry pbe1 = new PriceBookEntry(PriceBook2Id = Test.getStandardPricebookId(), 
                                                   Product2Id = prod1.Id,   
                                                   UnitPrice=100, 
                                                   IsActive=true);
        INSERT pbe1;                                           
         
        // create product
        Product2 prod2  = new Product2();
        prod2.Name      = 'test product 2';
        prod2.Family    = 'Device';
        prod2.IsActive 	= true;        
        INSERT prod2;

       // create price book entry for this product
       PriceBookEntry pbe2 = new PriceBookEntry(PriceBook2Id = Test.getStandardPricebookId(), 
                                                  Product2Id = prod2.Id,   
                                                  UnitPrice=100, 
                                                  IsActive=true);

        INSERT pbe2;

        // Create 1st OLI with licensed product --> this will trigger after insert trigger
		OpportunityLineItem oli1 = new OpportunityLineItem();
		oli1.OpportunityId       = opp.Id;
        oli1.Quantity            = 2;        
        oli1.PriceBookEntryId    = pbe1.Id;
        oli1.UnitPrice           = pbe1.UnitPrice;    
		INSERT oli1; 

        // Create 2nd OLI with licensed product --> this will trigger after insert trigger
		OpportunityLineItem oli2 = new OpportunityLineItem();
		oli2.OpportunityId       = opp.Id;
        oli2.Quantity            = 2;        
        oli2.PriceBookEntryId    = pbe2.Id;
        oli2.UnitPrice           = pbe2.UnitPrice;    
		INSERT oli2; 

    }

    // method 2 - delete OLI's
        private static testMethod void deleteOlis(){

        // create account
        Account act = new Account();
        act.Name    = 'test account';
        INSERT act;

        //create Opportunity
        Opportunity opp  = new Opportunity();
        opp.Name 		 = 'test Opp';
        opp.AccountId    = act.Id;
        opp.Type 		 = 'System';
        opp.RecordTypeId = [SELECT id 
                              FROM RecordType 
                             WHERE SObjectType='Opportunity' 
                               AND DeveloperName=:opp.type].id; 
       opp.StageName     = 'Prospecting';
       opp.CloseDate     = Date.today();
       INSERT opp;

       // create product
       Product2 prod1 	 = new Product2();
       prod1.Name        = 'test product 1';
       prod1.Family      = 'Systems';
       prod1.IsActive 	 = true;        
       INSERT prod1;

      // create price book entry for this product
      PriceBookEntry pbe1 = new PriceBookEntry(PriceBook2Id = Test.getStandardPricebookId(), 
                                                 Product2Id = prod1.Id,   
                                                 UnitPrice=100, 
                                                 IsActive=true);
      INSERT pbe1;                                           
       
      // create product
      Product2 prod2  = new Product2();
      prod2.Name      = 'test product 2';
      prod2.Family    = 'Device';
      prod2.IsActive  = true;        
      INSERT prod2;

     // create price book entry for this product
     PriceBookEntry pbe2 = new PriceBookEntry(PriceBook2Id = Test.getStandardPricebookId(), 
                                                Product2Id = prod2.Id,   
                                                UnitPrice=100, 
                                                IsActive=true);
      INSERT pbe2;                                          

      // Create 1st OLI with licensed product --> this will trigger after insert trigger
      OpportunityLineItem oli1 = new OpportunityLineItem();
      oli1.OpportunityId       = opp.Id;
      oli1.Quantity            = 2;        
      oli1.PriceBookEntryId    = pbe1.Id;
      oli1.UnitPrice           = pbe1.UnitPrice;    
      INSERT oli1; 

      // Create 2nd OLI with licensed product --> this will trigger after insert trigger
      OpportunityLineItem oli2 = new OpportunityLineItem();
      oli2.OpportunityId       = opp.Id;
      oli2.Quantity            = 2;        
      oli2.PriceBookEntryId    = pbe2.Id;
      oli2.UnitPrice           = pbe2.UnitPrice;    
      INSERT oli2; 

      // now we need to run before delete trigger, thus delete the Olis now
      DELETE oli1;
      DELETE oli2;    
    }

}

 
Ember Campbell 10Ember Campbell 10
I deleted the trigger, class and outbound change set to see if the issue was with something I had tried before.  Rebuilt all three with the same results that the incoming change set has zero coverage.  I also tried a new sandbox to see if the issue was with something getting stuck with all my testing.  Same results with zero code coverage.

Sorry, I'm not sure how to do the following: Can you put system.debug() after the SELECT query and see if you are getting any data.

I see system.debug listed in the trigger already.  Should this be listed in the class as well?