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
DipthiDipthi 

Need help with test coverage for a loop

Hi Coders, I am new to Coding and took a task to write a test class. I am working for a Leave management App and made my coverage for 65%. I am not understading why my for loop is not covered in coverage. your help is greatly appreciated !!


This my Class: (Bold code is NOT in coverage)
trigger OnLeaveApproval on Leaves__c (after update)
{
    
for(Leaves__c obj: Trigger.new)
{
    map<string, Object> leaveMap = new map<string, Object>();
    system.debug('coming here 1');   
    if (obj.Approval_Status__c != Null && obj.Approval_Status__c == 'Approved')
    {
            system.debug('coming here 1.2'+ obj.CreatedById); 
            system.debug('coming here 1.2'+ obj.Contact_ID__c); 
            system.debug('name'+ obj.Name);
            system.debug('coming here 1.2.1'+ obj); 
            leaveMap.put(obj.Contact_ID__c, obj.Req_Days_Off__c);
            system.debug('coming here 2'); 
    }
    system.debug('coming here 2.1' + leaveMap);
    if(leaveMap.size() > 0 ) {
  
            list<Contact> contact = [SELECT Id,Total_Available_Leaves__c,OwnerId FROM Contact WHERE  Id IN :leaveMap.KeySet()];
            system.debug('coming here 3' + contact); 
            list<Monthly_Take_Home__c> employeePayroll = [SELECT Id,Unpaid_Days__c, Paid_Days__c, Salary__c,Emp_Salary__c,Current_PayPeriod_Salary__c FROM Monthly_Take_Home__c WHERE Contact_ID__c IN :leaveMap.KeySet()];
            system.debug('coming here 3.1' + employeePayroll);
            list<Contact> contactUpdatelist = new list<Contact>();
            list<Monthly_Take_Home__c> employeePayrolllist = new list<Monthly_Take_Home__c>();
            for(Contact c: contact)
                {
                    system.debug('coming here 4'+ contact); 
                    //Integer totalApprovedLeaves = leaveMap.get(c.Name);
                    Integer totalApprovedLeaves = Integer.valueOf(leaveMap.get(c.Id));
                    if( c.Total_Available_Leaves__c - totalApprovedLeaves < 0 ) {
                        
                            system.debug('coming here 5'+c.Total_Available_Leaves__c); 
                             Integer lossOfPayDays = totalApprovedLeaves - c.Total_Available_Leaves__c.intValue();
                             system.debug('coming here 5.1'+ lossOfPayDays);
                            for(Monthly_Take_Home__c empPayroll: employeePayroll)
                            {
                                
                                  if(empPayroll.Unpaid_Days__c !=null)
                                      lossOfPayDays = lossOfPayDays + empPayroll.Unpaid_Days__c.intValue();
                                system.debug('coming here 5.1'+ lossOfPayDays);
                                system.debug('coming here 6.0'+empPayroll.Paid_Days__c); 
                                Integer totalPayDates = empPayroll.Paid_Days__c.intValue() - lossOfPayDays;
                                system.debug('totalPayDates 6'+totalPayDates); 
                                Double calculatedSalary =  Double.valueOf(empPayroll.Emp_Salary__c/empPayroll.Paid_Days__c.intValue());
                                system.debug('empPayroll.Salary__c 0'+ empPayroll.Emp_Salary__c);
                                system.debug('totalPayDates'+ totalPayDates);
                                system.debug('lossOfPayDays'+ lossOfPayDays);
                                empPayroll.Current_PayPeriod_Salary__c = (calculatedSalary * totalPayDates) ;
                                system.debug('totalSalary'+ calculatedSalary * totalPayDates);
                                empPayroll.Unpaid_Days__c = lossOfPayDays;
                                system.debug('empPayroll.Current_PayPeriod_Salary__c'+ empPayroll.Current_PayPeriod_Salary__c);
                                system.debug('calculatedSalary'+ calculatedSalary);     
                                employeePayrolllist.add(empPayroll);
                            }
                        c.Total_Available_Leaves__c = 0;
                    } else {
                        
                        c.Total_Available_Leaves__c =
c.Total_Available_Leaves__c - totalApprovedLeaves;
                    }
                    contactUpdatelist.add(c);
            }
            if(contactUpdatelist.size() > 0)
            {
                system.debug('coming here 7'+ contactUpdatelist); 
                update contactUpdatelist;
            }

            if(employeePayrolllist.size() > 0)
            {
                system.debug('coming here 8'+ employeePayrolllist); 
                update employeePayrolllist;
            }
        }
}
}


This my Test Class: Covered 65% of code
@isTest
private class OnLeaveApprovalTest {
 
@isTest
public static void CreateContactAndLeaveReq(){
test.startTest(); 

// Test data setup - Create a new Employee contact
Contact emp = new Contact ();
emp.FirstName = 'Jon';
emp.LastName = 'Snow';
emp.Emp_ID__c = 'Sp-343245';
emp.Total_Available_Leaves__c = 4;
emp.Emp_Salary__c = 10000;
    
insert emp;
system.debug(emp);

Contact EmpInfo = [Select Name,Id from Contact Where id =: Emp.Id]; 

//Enter record details on Leaves Object
Leaves__c leaveReq = new Leaves__c();
leaveReq.Employee_Name__c = EmpInfo.Id;
leaveReq.Request_Start_Date__c = date.newInstance(2020,06,10);
leaveReq.Request_End_Date__c = date.newInstance(2020,06,15);
leaveReq.Contact_ID__c = Emp.Id;

Insert leaveReq;
system.debug(leaveReq);


leaveReq.Approval_Status__c = 'Approved';
Update leaveReq;
    
Monthly_Take_Home__c TakeHome = new Monthly_Take_Home__c();
TakeHome.Employee_Name__c = EmpInfo.Id;
TakeHome.Salary_Start_Date__c = date.newInstance(2020,06,01);
TakeHome.Salary_End_Date__c = date.newInstance(2020,06,30);
TakeHome.Unpaid_Days__c = 6;
TakeHome.Contact_ID__c = Emp.Id;

Insert TakeHome;
test.stopTest();   
}
}

Best Answer chosen by Dipthi
Maharajan CMaharajan C
HI Deepthi,

First Insert the Monthly_Take_Home__c record then update the leaveReq to Approved.

The test class should have the below order to cover that for loop:
@isTest
private class OnLeaveApprovalTest {
 
@isTest
public static void CreateContactAndLeaveReq(){
test.startTest(); 

// Test data setup - Create a new Employee contact
Contact emp = new Contact ();
emp.FirstName = 'Jon';
emp.LastName = 'Snow';
emp.Emp_ID__c = 'Sp-343245';
emp.Total_Available_Leaves__c = 4;
emp.Emp_Salary__c = 10000;
    
insert emp;
system.debug(emp);

Contact EmpInfo = [Select Name,Id from Contact Where id =: Emp.Id]; 

//Enter record details on Leaves Object
Leaves__c leaveReq = new Leaves__c();
leaveReq.Employee_Name__c = EmpInfo.Id;
leaveReq.Request_Start_Date__c = date.newInstance(2020,06,10);
leaveReq.Request_End_Date__c = date.newInstance(2020,06,15);
leaveReq.Contact_ID__c = Emp.Id;

Insert leaveReq;
system.debug(leaveReq);

Monthly_Take_Home__c TakeHome = new Monthly_Take_Home__c();
TakeHome.Employee_Name__c = EmpInfo.Id;
TakeHome.Salary_Start_Date__c = date.newInstance(2020,06,01);
TakeHome.Salary_End_Date__c = date.newInstance(2020,06,30);
TakeHome.Unpaid_Days__c = 6;
TakeHome.Contact_ID__c = Emp.Id;

Insert TakeHome;

/// once inserted the TakeHome then u have to update the leaveReq
leaveReq.Approval_Status__c = 'Approved';
Update leaveReq;   
    
test.stopTest();   
}
}
    


Thanks,
Maharajan.C

All Answers

Maharajan CMaharajan C
HI Deepthi,

First Insert the Monthly_Take_Home__c record then update the leaveReq to Approved.

The test class should have the below order to cover that for loop:
@isTest
private class OnLeaveApprovalTest {
 
@isTest
public static void CreateContactAndLeaveReq(){
test.startTest(); 

// Test data setup - Create a new Employee contact
Contact emp = new Contact ();
emp.FirstName = 'Jon';
emp.LastName = 'Snow';
emp.Emp_ID__c = 'Sp-343245';
emp.Total_Available_Leaves__c = 4;
emp.Emp_Salary__c = 10000;
    
insert emp;
system.debug(emp);

Contact EmpInfo = [Select Name,Id from Contact Where id =: Emp.Id]; 

//Enter record details on Leaves Object
Leaves__c leaveReq = new Leaves__c();
leaveReq.Employee_Name__c = EmpInfo.Id;
leaveReq.Request_Start_Date__c = date.newInstance(2020,06,10);
leaveReq.Request_End_Date__c = date.newInstance(2020,06,15);
leaveReq.Contact_ID__c = Emp.Id;

Insert leaveReq;
system.debug(leaveReq);

Monthly_Take_Home__c TakeHome = new Monthly_Take_Home__c();
TakeHome.Employee_Name__c = EmpInfo.Id;
TakeHome.Salary_Start_Date__c = date.newInstance(2020,06,01);
TakeHome.Salary_End_Date__c = date.newInstance(2020,06,30);
TakeHome.Unpaid_Days__c = 6;
TakeHome.Contact_ID__c = Emp.Id;

Insert TakeHome;

/// once inserted the TakeHome then u have to update the leaveReq
leaveReq.Approval_Status__c = 'Approved';
Update leaveReq;   
    
test.stopTest();   
}
}
    


Thanks,
Maharajan.C
This was selected as the best answer
Maharajan CMaharajan C

Deepthi, One more suggestion from my side the above trigger not written in bulkified way. So it will easily hit the Salesforce Governor Limits when you updating the bulk Leaves__c. (It will hit SOQL Limit. DML Limits easily)

1. Soql inside the for loop is not good practice.
2. DML inside the for loop is not good practice.
3. For Loop inside the for loop.
4. Use the helper class to handle the trigger logics

So try to avoid the above limits and write the code to handle the bulk data. Learn the collections in apex class 

Refer the below posts for handle the soql and dml:

https://www.appseconnect.com/salesforce-apex-best-practices/#:~:text=Avoid%20SOQL%20Queries%20or%20DML%20Statements%20inside%20'FOR%20Loops'%20%3A&text=When%20SOQL%20queries%20and%20DML,avoid%20these%20scenarios%20whenever%20possible.

https://developer.salesforce.com/page/Best_Practice%3A_Avoid_SOQL_Queries_Inside_FOR_Loops
https://www.sfdc99.com/2014/01/20/soql-queries-inside-loops/
https://www.solunus.com/apex-best-practices
https://11concepts.com/sfdc/blog/use-of-sets-and-maps-to-avoid-force-com-governor-limits/


Thanks,
Maharajan.C

Andrew GAndrew G
@maharajan
Can you explain your use of Test.startTest();   ?  
Why is it at the very start of your test method? Before data setup?

Is it not best practice to actually set up your test data, and then invoke the Test.startTest() when you are actually about to run the code to be tested?
In this manner you reset the Governor Limits for the code being tested.  This would give a true indication of the behaviour of the code being tested.  In more complex code testing, if you have chewed through 90 SOQLS just setting up your test data, you get 10 more just to do the actual code.   By doing the data setup, and then placing the line Test.startTest() we reset the SOQL and DML governor limits so that the code being tested is accurately assessed, especially if we are also do bulk testing.


Also, as an aside, if we are doing test code for people, should we not also be showing how to use asserts to confirm that the code behaves as expected?


Interested in your reply.

Regards
AndrewG

 
Maharajan CMaharajan C
Apologize Andrew... I have just verified why the for loop is not covering. Not focused where deepthi used the test.startTest(); and test.stopTest();

Here after i will use the Assert and test.startTest(); , test.stopTest()  in properly.

Thanks,
Maharajan.C
DipthiDipthi

@Maharajan,

Thank you very much for guiding me!!  It really worked and code coverage is 96%. Will also make changes to my code as you suggested.

@Andrew, Thank you very much for Ietting me know key points in Test Class. I now really got to know when to use test.startTest() and will also modify with Assert to check the result. 

Andrew GAndrew G
Apologies if I seem terse, but i feel that when we come onto these forums, for that moment where we answer someone's question, we are a mentor to that person.  And therefore we should be presenting information that is correct and guiding the question asker to a better place in their understanding of SF.  For people who don't understand or know better, if we present poorly designed code as an answer, that is the code that will go into that environment, and then cause headaches down the path.

Regards
Andrew