You need to sign in to do that
Don't have an account?
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();
}
}
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:
Thanks,
Maharajan.C
All Answers
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:
Thanks,
Maharajan.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
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
Here after i will use the Assert and test.startTest(); , test.stopTest() in properly.
Thanks,
Maharajan.C
@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.
Regards
Andrew