+ Start a Discussion
Abhijit Shrikhande 10Abhijit Shrikhande 10 

Improve Test Coverage for a batch class

I have a batch class that runs once a day to send me emails.
global class getCreatedLeadsSummary implements Database.Batchable<AggregateResult>, Database.Stateful {
    public string leadSummary;
    global Iterable<AggregateResult> start(Database.batchableContext info){
        // just instantiate the new iterable here and return
        return new AggregateResultIterable();
    }

    global void execute(Database.BatchableContext BC, List<AggregateResult> scope)
    {
        try
        {
            leadSummary = '<table bgcolor="silver" border="2" cellpadding="2"><tr><td><b>Lead Source</b></td><td><b>Count</b></td></tr>';
            for(AggregateResult ar: scope)
            {
                string src = (string) ar.get('LeadSource');
                Integer cnt = (Integer) ar.get('cnt');
                leadSummary = leadSummary + '<tr><td>' + src + '</td><td>'+ cnt.format() +'</td></tr>';
            }
            leadSummary = leadSummary + '</table>';            
        }
        catch(Exception ex)
        {
            System.Debug('Some Exception >'+ ex.getMessage());
        }
    }

    global void finish(Database.BatchableContext BC)
    {
        // Get the AsyncApexJob using JobId 
        AsyncApexJob a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed, TotalJobItems, CreatedBy.Email, ExtendedStatus   
                            FROM AsyncApexJob 
                            WHERE Id = :BC.getJobId()];  
                                    
        if(a.Status == 'Completed')           
            utilMail.sendNotificationMail('Lead Summary Job Result',a,leadSummary);   
        else
            utilMail.sendNotificationMail('Job Did not Complete!',a,'Failure');   
    }   

    global class AggregateResultIterable implements Iterable<AggregateResult> {
        global Iterator<AggregateResult> Iterator(){
            return new AggregateResultIterator();
       }
    }

    global class AggregateResultIterator implements Iterator<AggregateResult> {
        AggregateResult [] results {get;set;}
        Integer index {get; set;} 

        global AggregateResultIterator() {
            index = 0;
            String query = 'Select COUNT(Id) cnt, LeadSource From Lead WHERE CreatedDate = YESTERDAY GROUP BY LeadSource';
            results = Database.query(query);            
        } 
        
        global boolean hasNext(){ 
           return results != null && !results.isEmpty() && index < results.size(); 
        }    
        
        global AggregateResult next(){ 
            return results[index++];            
        }       
    }    
}

This is my test metohd I created.
 
static testmethod void testmethod1() {
            
            List<Lead> allLeads = new List<Lead>();
            for(integer i =0; i< 199; i++)
            {
                            Lead l = new Lead();
                            l.FirstName = 'TEST ' + i.format();
                            l.LastName = 'Lead';
                            l.Lease_Amount__c = 240;
                            l.LeadSource='2055 - Website';
                            l.Company = 'Testing Batch Jobs';   
                            l.Title = 'Dr';
                            l.Phone='5556667777';
                            allLeads.Add(l);
            }
       
       insert allLeads;

       Test.startTest();
       Database.executeBatch(new getCreatedLeadsSummary());     
       Test.stopTest();

       Integer i = [SELECT COUNT() FROM Lead WHERE CreatedDate=Today];
       System.Debug(i);       
       System.assertEquals(i, 199);
    }
After executing this test method, my coverage is at 56% (17/30) lines. So I found that the execute method is not being covered. How can I cover the execute method in my test class and attempt to reach 100% coverage?
 
Best Answer chosen by Abhijit Shrikhande 10
R Z KhanR Z Khan
Please refer to the dev guide.
Currently you are not passing any query so there is nothing to exectue and your batch doesnt work. thats why your coverage is low
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_interface.htm
 
global class SummarizeAccountTotal implements 
    Database.Batchable<sObject>, Database.Stateful{

   global final String Query;
   global integer Summary;
  
   global SummarizeAccountTotal(String q){Query=q;
     Summary = 0;
   }

   global Database.QueryLocator start(Database.BatchableContext BC){
      return Database.getQueryLocator(query);
   }
   
   global void execute(
                Database.BatchableContext BC, 
                List<sObject> scope){
      for(sObject s : scope){
         Summary = Integer.valueOf(s.get('total__c'))+Summary;
      }
   }

global void finish(Database.BatchableContext BC){
   }
}

Mark question as reasolved if you dont have anymore questions

All Answers

R Z KhanR Z Khan
Hi, your execute doesnt run because you never specified what to query in your batch class. So it has nothing to execute. Pass your query in constructor or hard code it in your batch class. 
getCreatedLeadsSummary implements Database.Batchable<AggregateResult>, 
    private string query;

public getCreatedLeadsSummary(String query){
this.query = query;
}

    global Iterable<AggregateResult> start(Database.batchableContext info){
        // just instantiate the new iterable here and return
        return new AggregateResultIterable(query);
    }

 
Abhijit Shrikhande 10Abhijit Shrikhande 10
The getCreatedLeadsSummary is a Stateful class. Are you suggesting making changes to this class instead of the test class?
R Z KhanR Z Khan
oh right. then change the variable to 

global final String query;

andcall your batch as following
getCreatedLeadsSummary batch = new getCreatedLeadsSummary();
batch.query='select Id, Name FROM Lead';
Database.executeBatch(batch);

 
Shikha AgashiShikha Agashi
No, i am telling you update your test data with createddate. You are missing it. your following query looks for records from yesterday. 

String query = 'Select COUNT(Id) cnt, LeadSource From Lead WHERE CreatedDate = YESTERDAY GROUP BY LeadSource';

Secondly:
//if(a.Status == 'Completed') // Please create status with different values while creating lead data.

To make test class run make sure your test data has all values that are required to run your code logic.
Abhijit Shrikhande 10Abhijit Shrikhande 10
Shikha,
How can I update the CREATED DATE on test data? Isn't created date a foot print column? If I change the query, it won't address the business problem. Secondly, status is column on the AsyncApexJob table. Different values are not required here.

RZ,
How can I assign a value to a variable if it is declared with the final keyword in the class? I will have to end up changing the constructor or writing a new one in this case.
R Z KhanR Z Khan
Please refer to the dev guide.
Currently you are not passing any query so there is nothing to exectue and your batch doesnt work. thats why your coverage is low
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_interface.htm
 
global class SummarizeAccountTotal implements 
    Database.Batchable<sObject>, Database.Stateful{

   global final String Query;
   global integer Summary;
  
   global SummarizeAccountTotal(String q){Query=q;
     Summary = 0;
   }

   global Database.QueryLocator start(Database.BatchableContext BC){
      return Database.getQueryLocator(query);
   }
   
   global void execute(
                Database.BatchableContext BC, 
                List<sObject> scope){
      for(sObject s : scope){
         Summary = Integer.valueOf(s.get('total__c'))+Summary;
      }
   }

global void finish(Database.BatchableContext BC){
   }
}

Mark question as reasolved if you dont have anymore questions
This was selected as the best answer
Abhijit Shrikhande 10Abhijit Shrikhande 10
Hello RZ,
My batch does work. It works just the way I want it to. The issue I am facing is with the test class.