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
Robert BerkeleyRobert Berkeley 

Apex - Creating a test class for a batch class

Hello
I have pulled together a Batchable APEX class below that generates missing Contacts from a list of Accounts:
global class GenerateContacts implements 
    Database.Batchable<sObject>, Database.Stateful{

   global final String Query;
   global integer count;
  
   global GenerateContacts(String q){Query=q;
     count = 0;
   }

   global Database.QueryLocator start(Database.BatchableContext BC){
      return Database.getQueryLocator(query);
   }
   
    global void execute(Database.BatchableContext BC,List<sObject> scope){
        Integer sp;
        String ln;
        String fn;
        for(sObject s : scope){
            Account a = (Account)s;
            if(a.Contacts.isEmpty()){
                System.debug('email: ' + a.Email_2__c);
                System.debug('name: ' + a.Name);
                sp = a.Name.LastIndexOf(' ');
                if (sp > 0) {
                    ln = a.Name.substring(sp);
                    fn = a.Name.substring(0, sp);
                }
                sObject ct = new Contact(AccountId = a.Id, Email = a.Email_2__c, FirstName = fn, LastName = ln);
                insert ct;
            }
            else
                count=count+1;
        }
    }

    global void finish(Database.BatchableContext BC){
        AsyncApexJob a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed,TotalJobItems, CreatedBy.Email
            FROM AsyncApexJob WHERE Id = :BC.getJobId()];
    
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        String[] toAddresses = new String[] {'me@mydomain.net'};
        mail.setToAddresses(toAddresses);
        mail.setSubject('Contacts Generation ' + a.Status);
        mail.setPlainTextBody('The batch Apex job processed ' + a.TotalJobItems +' batches with '+ a.NumberOfErrors + ' failures. Count was: '+count);
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }
}

My problem is with generating a Test class with greater than 75% coverage.  Here is my Test class:
@isTest
private class Test_Generate_Contacts {
 
    static testMethod void test_GenerateContacts() {
        String Query = 'Select a.Id, Email_2__c, First_Name__c, Last_Name__c, Name, (Select Id From Contacts limit 1) from Account a LIMIT 1';

        test.startTest();
        GenerateContacts gen=new GenerateContacts(Query); 
        String jobid = Database.executeBatch(gen);
        test.stopTest();
    }
}

This one only gives me 64%.  How can I get it higher?
Best Answer chosen by Robert Berkeley
Robert BerkeleyRobert Berkeley
Got the complete solution for this now:
global class GenerateContacts implements 
    Database.Batchable<sObject>, Database.Stateful{

   global final String Query;
   global integer count;
  
   global GenerateContacts(String q){Query=q;
     count = 0;
   }

   global Database.QueryLocator start(Database.BatchableContext BC){
      return Database.getQueryLocator(query);
   }
   
    global void execute(Database.BatchableContext BC,List<sObject> scope){
        Integer sp;
        String ln;
        String fn;
        for(sObject s : scope){
            Account a = (Account)s;
            if(a.Contacts.isEmpty()){
                System.debug('email: ' + a.Email_2__c);
                System.debug('name: ' + a.Name);
                sp = a.Name.LastIndexOf(' ');
                if (sp > 0) {
                    ln = a.Name.substring(sp);
                    fn = a.Name.substring(0, sp);
                }
                if (fn.length() < 41 && ln.length() < 41) {
                    sObject ct = new Contact(AccountId = a.Id, Email = a.Email_2__c, FirstName = fn, LastName = ln);
                    insert ct;
                }
            }
            else
                count=count+1;
        }
    }

    global void finish(Database.BatchableContext BC){
        AsyncApexJob a;
        if (BC != null) {
            a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed,TotalJobItems, CreatedBy.Email
                FROM AsyncApexJob WHERE Id = :BC.getJobId()];
        }
        
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        String[] toAddresses = new String[] {'me@mydomain.net'};
        mail.setToAddresses(toAddresses);
        if (a != null) {
            mail.setSubject('Contacts Generation ' + a.Status);
            mail.setPlainTextBody('The batch Apex job processed ' + a.TotalJobItems +' batches with '+ a.NumberOfErrors + ' failures. Count was: '+count);
        } else {
            mail.setSubject('Contacts Generation Test');
            mail.setPlainTextBody('Test');
        }
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }
}

And to get enough test coverage I had to call each batch method individually:
@isTest
private class Test_Generate_Contacts {
 
    static testMethod void test_GenerateContacts() {
        sObject act = new Account(Name = 'test man', First_Name__c = 'test', Last_Name__c = 'man', Email_2__c = 'test@test.com');
        insert act;
        System.debug('account id before: ' + act.Id);
        
        test.startTest();
        String Query = 'Select a.Id, Email_2__c, First_Name__c, Last_Name__c, Name, (Select Id From Contacts limit 1) from Account a where a.Id = \'' + act.Id + '\'';
        GenerateContacts gen=new GenerateContacts(Query);
        Database.BatchableContext bc;
        gen.start(bc);
        List<Account> accs = Database.query(Query);
        gen.execute(bc,accs);
        gen.finish(bc);
        test.stopTest();

        System.debug('account id after: ' + act.Id);
        List<Contact> c = Database.query('SELECT AccountId FROM Contact WHERE AccountId = \'' + act.Id + '\'');
        System.assertEquals(c[0].AccountId, act.Id);
    }
}


All Answers

NehalNehal (Salesforce Developers) 
Hi,

Please go through links below that will help you to increase the code coverage:

1.https://developer.salesforce.com/forums/ForumsMain?id=906F00000008wa9IAA
2.http://salesforce.stackexchange.com/questions/22337/test-class-for-batch-apex
3.http://salesforce.stackexchange.com/questions/8113/testing-batch-apex
4.http://salesforce.stackexchange.com/questions/22337/test-class-for-batch-apex
5.http://salesforce.stackexchange.com/questions/1609/how-can-i-get-code-coverage-programatically-through-apex
6.http://forcebydesign.com/technology/apex-code-coverage-75-or-bust/

I hope this helps.

Please mark this as a "Best Answer" if this has resolved your issue.
Chaten Raghav 9Chaten Raghav 9
Hi,


I checked for the issue and would like to share the apex test best practices and example illustration to write a test class for batch apex.
Please check :- 
1:- https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_testing_best_practices.htm  For best Practices.
2:- http://www.salesforce.com/us/developer/docs/apex_workbook/Content/apex_batch_2.htm   For test example of batch apex.

Please Mark it as ""Best answer"" if it help you.

Thanks

Robert BerkeleyRobert Berkeley
Got the complete solution for this now:
global class GenerateContacts implements 
    Database.Batchable<sObject>, Database.Stateful{

   global final String Query;
   global integer count;
  
   global GenerateContacts(String q){Query=q;
     count = 0;
   }

   global Database.QueryLocator start(Database.BatchableContext BC){
      return Database.getQueryLocator(query);
   }
   
    global void execute(Database.BatchableContext BC,List<sObject> scope){
        Integer sp;
        String ln;
        String fn;
        for(sObject s : scope){
            Account a = (Account)s;
            if(a.Contacts.isEmpty()){
                System.debug('email: ' + a.Email_2__c);
                System.debug('name: ' + a.Name);
                sp = a.Name.LastIndexOf(' ');
                if (sp > 0) {
                    ln = a.Name.substring(sp);
                    fn = a.Name.substring(0, sp);
                }
                if (fn.length() < 41 && ln.length() < 41) {
                    sObject ct = new Contact(AccountId = a.Id, Email = a.Email_2__c, FirstName = fn, LastName = ln);
                    insert ct;
                }
            }
            else
                count=count+1;
        }
    }

    global void finish(Database.BatchableContext BC){
        AsyncApexJob a;
        if (BC != null) {
            a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed,TotalJobItems, CreatedBy.Email
                FROM AsyncApexJob WHERE Id = :BC.getJobId()];
        }
        
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        String[] toAddresses = new String[] {'me@mydomain.net'};
        mail.setToAddresses(toAddresses);
        if (a != null) {
            mail.setSubject('Contacts Generation ' + a.Status);
            mail.setPlainTextBody('The batch Apex job processed ' + a.TotalJobItems +' batches with '+ a.NumberOfErrors + ' failures. Count was: '+count);
        } else {
            mail.setSubject('Contacts Generation Test');
            mail.setPlainTextBody('Test');
        }
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }
}

And to get enough test coverage I had to call each batch method individually:
@isTest
private class Test_Generate_Contacts {
 
    static testMethod void test_GenerateContacts() {
        sObject act = new Account(Name = 'test man', First_Name__c = 'test', Last_Name__c = 'man', Email_2__c = 'test@test.com');
        insert act;
        System.debug('account id before: ' + act.Id);
        
        test.startTest();
        String Query = 'Select a.Id, Email_2__c, First_Name__c, Last_Name__c, Name, (Select Id From Contacts limit 1) from Account a where a.Id = \'' + act.Id + '\'';
        GenerateContacts gen=new GenerateContacts(Query);
        Database.BatchableContext bc;
        gen.start(bc);
        List<Account> accs = Database.query(Query);
        gen.execute(bc,accs);
        gen.finish(bc);
        test.stopTest();

        System.debug('account id after: ' + act.Id);
        List<Contact> c = Database.query('SELECT AccountId FROM Contact WHERE AccountId = \'' + act.Id + '\'');
        System.assertEquals(c[0].AccountId, act.Id);
    }
}


This was selected as the best answer