+ Start a Discussion
Robert HainesRobert Haines 

I am totally stuck, cant' see to get code coverage for this Apex trigger...

This trigger is designed to count activities on a lead, like # of calls, # of emails, etc. The code works well, but I can't for the life of me seem to be able to create a test class that gets sufficient code coverage. I get like 9% code coverage by creating a lead and creating a task in my test code. PLEASE SHOW ME HOW IT'S DONE if you would, please. 

trigger FS_CountActivitiesOL on Lead (before update) 
{
    for(Lead lead : trigger.new) 
    {
        
        id thisLeadID = lead.id;
        
        List<Task> tasklist = [SELECT Type, CallDurationInSeconds, ActivityDate, FS_Connect__c From Task WHERE IsClosed = True AND whoid = :thisLeadID ORDER BY ActivityDate ASC];    
        
        if (tasklist.size() > 0) {
            
            //Let's add up all the tasks
            integer i = 0;
            for(Task t : tasklist){
                i++;
            }
            lead.FS_Activity_Count__c = i;
            
            //Let's find the ACTIVITIES BEFORE FIRST CONNECT, EMAILS BEFORE FIRST CONNECT, CALLS BEFORE FIRST CONNECT, FIRST CONNECT DATE and FIRST CONNECT DURATION (IN SEC)
            integer abfc = 0; 
            integer ebfc = 0; 
            integer cbfc = 0; 
            for(Task t : tasklist){
               if (t.FS_Connect__c == FALSE) {
                   abfc++;
                   if (t.Type == 'Call') {
                       cbfc++;
                   }
                   ELSE IF (t.Type == 'Email') {
                       ebfc++;
                   }
               }
               Else {
                   if (t.Type == 'Call') {
                       cbfc++;
                   }
                   ELSE IF (t.Type == 'Email') {
                       ebfc++;
                   }
                   lead.FS_First_Connect_Date__c = t.ActivityDate;
                   lead.FS_First_Connect_Duration_Sec__c = t.CallDurationInSeconds;
                   abfc++;
                   break;
               }
            }
            lead.FS_Activities_Before_First_Connect__c = abfc;
            lead.FS_Calls_Before_First_Connect__c = cbfc;
            lead.FS_Emails_Before_First_Connect__c = ebfc;
            
            //Now let's get the FIST CALL DATE
            for(Task t : tasklist){
               if (t.Type == 'Call') {
                   lead.FS_First_Call_Date__c = t.ActivityDate;
                   break;
               }
            }
            
            //Now let's get the FIRST EMAIL DATE
            for(Task t : tasklist){
               if (t.Type == 'Email') {
                   lead.FS_First_Email_Date__c = t.ActivityDate;
                   break;
               }
            }
            
            //Now let's get the TOTAL # OF CALLS, TOTAL # OF EMAILS and TOTAL # OF CONNECTS
            integer tncalls = 0; 
            integer tnemails = 0; 
            integer tnconns = 0;         
            for(Task t : tasklist){
               IF (t.Type == 'Email') {
                   tnemails++;
               }
               ELSE IF (t.Type == 'Call') {
                   tncalls++;
               }
               IF (t.FS_Connect__c == TRUE) {
                   tnconns++;
               }
            }    
            lead.FS_Total_Number_of_Calls__c = tncalls;
            lead.FS_Total_Number_of_Emails__c = tnemails;
            lead.FS_Total_Number_of_Connects__c = tnconns;   

        }     
    }
}
Best Answer chosen by Robert Haines
Amit Chaudhary 8Amit Chaudhary 8
Please try below test class.
@isTest
private class TEST_FS_TRIGGERS
{
    private static testMethod void myUnitTestCall() 
    {
        
        Lead lead=new Lead(
                        LastName='Doe',
                        FirstName='John',
                        Company='Test',
                        Status='Converted',
                        LeadSource='Other');
        insert lead;

        Task t = new Task();
			t.OwnerId = UserInfo.getUserId();
			t.Subject='No Subject';
			t.Status='Not Started';
			t.Priority='Normal';  
			t.type='Call';
			t.whoId = lead.id ;
        insert t;  
        
			t.status = 'Completed';
		update t;
		
		
        Test.startTest();
		
			lead.FS_Activity_Count__c=1.00;
			update lead;
			
        Test.stopTest();

    }
    private static testMethod void myUnitTestEmail() 
    {
        
        Lead lead=new Lead(
                        LastName='Doe',
                        FirstName='John',
                        Company='Test',
                        Status='Converted',
                        LeadSource='Other');
        insert lead;

        Task t = new Task();
			t.OwnerId = UserInfo.getUserId();
			t.Subject='No Subject';
			t.Status='Not Started';
			t.Priority='Normal';  
			t.type='Email';
			t.whoId = lead.id ;
        insert t;  
        
			t.status = 'Completed';
		update t;
		
		
        Test.startTest();
		
			lead.FS_Activity_Count__c=1.00;
			update lead;
			
        Test.stopTest();

    }
	
    private static testMethod void myUnitTestEmailFSConnect() 
    {
        
        Lead lead=new Lead(
                        LastName='Doe',
                        FirstName='John',
                        Company='Test',
                        Status='Converted',
                        LeadSource='Other');
        insert lead;

        Task t = new Task();
			t.OwnerId = UserInfo.getUserId();
			t.Subject='No Subject';
			t.Status='Not Started';
			t.Priority='Normal';  
			t.type='Email';
			t.whoId = lead.id ;
			t.Fs_Connect__c = true;
        insert t;  
        
			t.status = 'Completed';
		update t;
		
		
        Test.startTest();
		
			lead.FS_Activity_Count__c=1.00;
			update lead;
			
        Test.stopTest();

    }
	
}
Let us know if this will help you
 

All Answers

Amit Chaudhary 8Amit Chaudhary 8
Post your test class which you are using
Robert HainesRobert Haines
Here's my test class...

@isTest(seeAllData=true) 
private class TEST_FS_TRIGGERS
{
    private static testMethod void myUnitTest() 
    {
        
        // create a Lead
        Lead lead=new Lead(
                        LastName='Doe',
                        FirstName='John',
                        Company='Test',
                        Status='Converted',
                        LeadSource='Other');

        insert lead;

        //create a Task
        Task t = new Task();
        t.OwnerId = UserInfo.getUserId();
        t.Subject='No Subject';
        t.Status='Not Started';
        t.Priority='Normal';  
        t.type='Call';
        t.whoId = lead.id ;

        insert t;  
        
        Test.startTest();
        lead.FS_Activity_Count__c=1.00;
        update lead;  
        Test.stopTest();

        System.AssertEquals('Call',t.type);
        System.AssertEquals(1.00,lead.FS_Activity_Count__c);

    }
}
 
JeffreyStevensJeffreyStevens
Before your update to the Lead - change the status to something that makes the .isClosed flag be true.  Your SOQL in the trigger is selecting only closed tasks - so I assume the SOQL is returning zero tasks during tests. 
Amit Chaudhary 8Amit Chaudhary 8
Please try below test class.
@isTest
private class TEST_FS_TRIGGERS
{
    private static testMethod void myUnitTest() 
    {
        
        Lead lead=new Lead(
                        LastName='Doe',
                        FirstName='John',
                        Company='Test',
                        Status='Converted',
                        LeadSource='Other');
        insert lead;

        Task t = new Task();
			t.OwnerId = UserInfo.getUserId();
			t.Subject='No Subject';
			t.Status='Not Started';
			t.Priority='Normal';  
			t.type='Call';
			t.whoId = lead.id ;
        insert t;  
        
			t.status = 'Completed';
		update t;
		
		
        Test.startTest();
		
			lead.FS_Activity_Count__c=1.00;
			update lead;
			
        Test.stopTest();

    }
}

 
Robert HainesRobert Haines
Hi Guys! Updating the task status to Completed got me to 72% code coverage! Thank you! Now we just need a little more, so I can actaully deploy this to production... here's what I'm seeing is still not convered...

User-added image
Amit Chaudhary 8Amit Chaudhary 8
Please try below test class.
@isTest
private class TEST_FS_TRIGGERS
{
    private static testMethod void myUnitTestCall() 
    {
        
        Lead lead=new Lead(
                        LastName='Doe',
                        FirstName='John',
                        Company='Test',
                        Status='Converted',
                        LeadSource='Other');
        insert lead;

        Task t = new Task();
			t.OwnerId = UserInfo.getUserId();
			t.Subject='No Subject';
			t.Status='Not Started';
			t.Priority='Normal';  
			t.type='Call';
			t.whoId = lead.id ;
        insert t;  
        
			t.status = 'Completed';
		update t;
		
		
        Test.startTest();
		
			lead.FS_Activity_Count__c=1.00;
			update lead;
			
        Test.stopTest();

    }
    private static testMethod void myUnitTestEmail() 
    {
        
        Lead lead=new Lead(
                        LastName='Doe',
                        FirstName='John',
                        Company='Test',
                        Status='Converted',
                        LeadSource='Other');
        insert lead;

        Task t = new Task();
			t.OwnerId = UserInfo.getUserId();
			t.Subject='No Subject';
			t.Status='Not Started';
			t.Priority='Normal';  
			t.type='Email';
			t.whoId = lead.id ;
        insert t;  
        
			t.status = 'Completed';
		update t;
		
		
        Test.startTest();
		
			lead.FS_Activity_Count__c=1.00;
			update lead;
			
        Test.stopTest();

    }
	
    private static testMethod void myUnitTestEmailFSConnect() 
    {
        
        Lead lead=new Lead(
                        LastName='Doe',
                        FirstName='John',
                        Company='Test',
                        Status='Converted',
                        LeadSource='Other');
        insert lead;

        Task t = new Task();
			t.OwnerId = UserInfo.getUserId();
			t.Subject='No Subject';
			t.Status='Not Started';
			t.Priority='Normal';  
			t.type='Email';
			t.whoId = lead.id ;
			t.Fs_Connect__c = true;
        insert t;  
        
			t.status = 'Completed';
		update t;
		
		
        Test.startTest();
		
			lead.FS_Activity_Count__c=1.00;
			update lead;
			
        Test.stopTest();

    }
	
}
Let us know if this will help you
 
This was selected as the best answer
Robert HainesRobert Haines
You guys are AMAZING! My code coverage is now 80%! 

Couple things I noticed, and maybe you can help address these too...

1) The t.Fs_Connect__c = true; part doesn't work because that field is a read-only formulat field, calculating it's value off another field. 

2) When more thant 100 leads are updated at one time (using Mass Update feature in Leads view), I get this error...
Too many SOQL queries: 101
Error is in expression '{!step5}' in component <apex:commandButton> in page massupdatelead: Class.FSLeadUpdateClass.RunFSLeadUpdate: line 7, column 1
Trigger.FS_CountActivitiesOL: line 5, column 1

If it's at all possible to work around that (since it's feasible to update 200 leads at a time with the Mass Update feature), it would be great to figure this out. I tried creating a class with the @future tag, but any classes with that have to be void and cannot accept SObjects as parameters, so that seems like a dead end. 
JeffreyStevensJeffreyStevens
Ya - your SOQL should never happen in a loop.

So, loop through all of the Leads that fired the trigger, and get their IDs.  Then get the list of tasks for those leads and put in a map like map<LeadId,list<task>>

Then you can loop through all of the Leads again - and for each lead get the list of tasks just for that Lead and loop through those.
Robert HainesRobert Haines
@JeffreyStevens I get that conceptually, but I am learning as I go here (only started figuring out Apex a few days ago). I'm not exactly sure how to implement what you just suggested... have never used Maps before. Any chance you could "show and tell" to give me an example? 
JeffreyStevensJeffreyStevens
Before - your first For Lead....
 
// Build map<LeadId,list<task>>
set<id> leadIds = new set<id>();
for(lead lead :trigger.new) {
  leadIds.add(lead.id);
}
list<task> tasks = new list<task>([SELECT Type,CallDurationInSeonds,ActivityDate,FS_Connect__c,whoId 
                                                            FROM Task
                                                            WHERE isClosed = True
                                                               AND whoid IN :leadIds
                                                         ]);
// Now put tasks in the map...
map<id,list<task>> mLeadIdTasks = new map<id,list<task>>();
for(task t :tasks) {
  list<task> iterTasks = new list<task>();
  if(mLeadIdTasks.containsKey(t.whoid)) {
    iterTasks = mLeadIdTasks.get(t.whoid);
  }
  iterTasks.add(t);
  mLeadIdTasks.put(t.whoId,iterTasks);
}

So - now - you have a filled in map.  Now - remove the SOQL that was in your loop - and anywere you were looping through tasks - retrieve the mLeadIdTasks map and loop throug them.


So instead of this.....
if (tasklist.size() > 0) {
            
            //Let's add up all the tasks
            integer i = 0;
            for(Task t : tasklist){
                i++;
            }
            lead.FS_Activity_Count__c = i;

You should have....
 
if(mLeadItTasks.containsKey(lead.id) {
  list<task> iterTasks = new list<task>();
  if(iterTasks.size()>0) {
    interger i = 0;
    for(Task t :iterTasks) {
      i++;
    }
    
    lead.FS_Activity_Count__ = i;


// etc....

You'll need to finish up the code and maybe some cleanup - but that should give you the basic idea.

 
Robert HainesRobert Haines
@JeffreyStevens -- I am sooooo close, but having an error with that code...

trigger FS_CountActivitiesOL on Lead (before update) 
{
    set<id> leadIds = new set<id>();

    for(Lead lead : trigger.new) 
    {
        leadIds.add(lead.id);
    }
    
    List<Task> tasks = [SELECT Type, CallDurationInSeconds, ActivityDate, FS_Connect__c From Task WHERE IsClosed = True AND whoid IN :leadIds ORDER BY ActivityDate ASC];    

    map<id,list<task>> mLeadIdTasks = new map<id,list<task>>();
    
    for(task t : tasks) 
    {
        List<task> iterTasks = new list<task>();
        if(mLeadIdTasks.containsKey(t.whoid)) 
        {
            iterTasks = mLeadIdTasks.get(t.whoid);
        }
        iterTasks.add(t);
        mLeadIdTasks.put(t.whoId,iterTasks);
    }
    
    if(mLeadIdTasks.containsKey(lead.id))
    {
        //this part is not working
        //Error: Compile Error: Incompatible key type Schema.SObjectField for Map<Id,List<Task>> at line 25 column 8    
    }
}  
JeffreyStevensJeffreyStevens
I see you found my missing ).

How about the integer typo?  
Robert HainesRobert Haines
I found that too, but was not using it at the moment... the part that is throwing me is:

    if(mLeadIdTasks.containsKey(lead.id))
    {
        //this part is not working
        //Error: Compile Error: Incompatible key type Schema.SObjectField for Map<Id,List<Task>> at line 25 column 8    
    }
JeffreyStevensJeffreyStevens
Oh oh - maybe this is better....
 
if(mLeadIdTasks.containsKey(lead.id)) {
  list<task> iterTasks = new list<task>();
  iterTasks = mLeadIdTasks.get(lead.id);
  if(iterTasks.size()>0) {
    integer i = 0;
    for(Task t :iterTasks) {
      i++;
    }
    lead.FS_Activity_Count__ = i;
// etc....

If that doesn't do it - paste back the code that you do have - (so I can see line #25).

 
Robert HainesRobert Haines
I'm getting Error: Compile Error: Incompatible key type Schema.SObjectField for Map<Id,List<Task>> at line 28 column 21. I don't think it knows what lead.id is. Do we need to reinstantiate it somehow? 
trigger FS_CountActivitiesOL on Lead (before update) 
{
    set<id> leadIds = new set<id>();

    for(Lead lead : trigger.new) 
    {
        leadIds.add(lead.id);
    }
    
    List<Task> tasks = [SELECT Type, CallDurationInSeconds, ActivityDate, FS_Connect__c From Task WHERE IsClosed = True AND whoid IN :leadIds]; // ORDER BY ActivityDate ASC];    

    map<id,list<task>> mLeadIdTasks = new map<id,list<task>>();
    
    for(task t : tasks) 
    {
        List<task> iterTasks = new list<task>();
        if(mLeadIdTasks.containsKey(t.whoid)) 
        {
            iterTasks = mLeadIdTasks.get(t.whoid);
        }
        iterTasks.add(t);
        mLeadIdTasks.put(t.whoId,iterTasks);
    }
    
    if(mLeadIdTasks.containsKey(lead.id)) 
    {
        list<task> iterTasks = new list<task>();
        iterTasks = mLeadIdTasks.get(lead.id);
        if(iterTasks.size()>0) 
        {
            integer i = 0;
            for(Task t :iterTasks) 
            {
                i++;
            }
            lead.FS_Activity_Count__c = i;
        }    
    }
}

 
JeffreyStevensJeffreyStevens
Oh - I didn't know that you weren't still in the Loop of leads - I think that was the issue.

But this code compiles on my system....
trigger test on Lead (before update) 
{
	
	
    set<id> leadIds = new set<id>();

    for(Lead lead : trigger.new) 
    {
        leadIds.add(lead.id);
    }
    
    List<Task> tasks = [SELECT Type, CallDurationInSeconds, ActivityDate
    						From Task 
    						WHERE IsClosed = True AND whoid IN :leadIds]; // ORDER BY ActivityDate ASC];    

    map<string,list<task>> mLeadIdTasks = new map<string,list<task>>();
    
    for(task t : tasks) 
    {
        List<task> iterTasks = new list<task>();
        if(mLeadIdTasks.containsKey(t.whoid)) 
        {
            iterTasks = mLeadIdTasks.get(t.whoid);
        }
        iterTasks.add(t);
        mLeadIdTasks.put(t.whoId,iterTasks);
    }

	for(Lead lead :trigger.new) {
    
	    if(mLeadIdTasks.containsKey(lead.id)) {
	        list<task> iterTasks = new list<task>();
	        iterTasks = mLeadIdTasks.get(lead.id);
	        if(iterTasks.size()>0) 
	        {
	            integer i = 0;
	            for(Task t :iterTasks) 
	            {
	                i++;
	            }
	            //lead.FS_Activity_Count__c = i;
	        } 
	        // Could just do this....
		    //lead.FS_Activity_Count__c = iterTasks.size(); 
		}
	}
}

 
Robert HainesRobert Haines
Ahhhh, I see you added a 2nd "for(Lead lead :trigger.new)" loop... makes sense. The code compiled, but now when I try to update a lead and run it, I get this error...

Error:Apex trigger FS_CountActivitiesOL caused an unexpected exception, contact your administrator: FS_CountActivitiesOL: execution of BeforeUpdate caused by: System.SObjectException: SObject row was retrieved via SOQL without querying the requested field: Task.WhoId: ()
JeffreyStevensJeffreyStevens
Ya  - I didn't have those fields in my LEAD object, so I took them out. Change your SOQL back to include all of your custom fields.

Add back in WhoId ( I must have left it off for some reason)
Robert HainesRobert Haines
THANK YOU, THANK YOU, THANK YOU! I was missing the SELECT WhoID part.