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
maylormaylor 

Recommended example and best practices for creating test class for an Apex batch class?

I've gotten over the first hurdle and was able to create a batch class and a schedule class that writes data to a custom target object. I found the following thread a great tutorial for the batch and schedule classes https://developer.salesforce.com/forums/ForumsMain?id=906F00000009FXSIA2.

Now I'd like to put the classes into production and need some help. I found the following article lacking for my specific example: http://www.salesforce.com/us/developer/docs/apex_workbook/Content/apex_batch_2.htm. In my circumstance I am using a query locator and am not finding test examples that align.

global class SnapshotOpenCasesBatch implements Database.Batchable<sObject> 
 {
	
	//Gather all the records I want to use in the execute method
	global Database.QueryLocator start(Database.BatchableContext BC) 
	{
        return Database.getQueryLocator([SELECT ID, OwnerID, Name, Case_Type__c, Subtype__c, Status__c, Reason__c, Case_Age_Days__c, CreatedDate FROM Custom_Case__c WHERE Status__c in ('New', 'Work in Process', 'Escalated', 'Solution Proposed/Validation') ORDER BY CreatedDate ASC]); // Production source field is Created_Date__c
	}
	
	global void execute(Database.BatchableContext BC, List<Custom_Case__c> openCases) 
	{
    
            //for creating new Snapshot batch.
            List<Snapshot_Open_CASE__c> newSnapshot = new List<Snapshot_Open_CASE__c>();
            
        	//create map collection to pair case owner ID and full name 
            Map<ID, String> userMap = new Map<ID, String>(); 
    
            //add all OwnerId values from openCases List to the set of userIds for query
            Set<ID> userIdSet = new Set<ID>();
            
            for (Custom_Case__c c : openCases) {
                userIdSet.add(c.OwnerID);
            }
         
            //Go get all the User ID + name combos from the set of Ids created above
            List<User> userList = new List<User>([SELECT ID, Name FROM User WHERE ID in : userIdSet]);
        
            //Load userMap with the results of the query above
            for(User u : userList){
                userMap.put(u.id, u.Name);
            }   
        
            for (Custom_Case__c c : openCases) {
                Snapshot_Open_CASE__c snap = new Snapshot_Open_CASE__c();
                snap.CASE_ID__c = c.ID;
                snap.OwnerID__c = c.OwnerID;
                snap.Case_Number__c = c.Name;
                snap.Case_Type__c = c.Case_Type__c;
                snap.Subtype__c = c.Subtype__c;
                snap.Status__c = c.Status__c;
                snap.Reason__c = c.Reason__c;
                snap.Case_Age_Days__c = c.Case_Age_Days__c;
                snap.Created_Date__c = c.CreatedDate.date(); // Production source field is Created_Date__c // also convert datetime to date
                snap.Name = String.valueof(Date.today()); // Use execution date as unique id
                
                // add the user name
                snap.Owner_Full_Name__c = userMap.get(c.OwnerId);
                    
                //load up the data for insert
                newSnapshot.add(snap);
        		}

    		insert newSnapshot;
			}
	
	global void finish(Database.BatchableContext BC) {
		system.debug('Batch Job is Complete');
	}
}
More high level, and nice to have but not critical, can someone explain the composition of how a test class should be constructed with respect to a batch? Don't need to get too much into theory but that would be helpful. For example, in some articles an 'a-ha' moment comes from adding a LIMIT 200 records clause to the SOQL, but what's the reasoning behind that?

Thanks!
robdobbyrobdobby
Hi!  You can execute a single batch in unit tests like this:
 
SnapshotOpenCasesBatch sbatch = new SnapshotOpenCasesBatch();
Id batchprocessid = Database.executeBatch(sbatch, 1);

If your SOQL in the start() method needs to be changed for tests you can add a static flag that is set in your unit test:
 
public Boolean isTest = false;

//Gather all the records I want to use in the execute method
global Database.QueryLocator start(Database.BatchableContext BC)
{
	String qry = 
		' SELECT ID, OwnerID, Name, Case_Type__c, Subtype__c, Status__c, Reason__c, Case_Age_Days__c, CreatedDate ' +
		' FROM Custom_Case__c ' +
		' WHERE Status__c in ('New', 'Work in Process', 'Escalated', 'Solution Proposed/Validation') ' +
		
	if (isTest) {
		qry += ' LIMIT 1 ';
	}
	else {
		qry += ' ORDER BY CreatedDate ASC ';
	}
	return Database.getQueryLocator(qry); // Production source field is Created_Date__c
	
}

The LIMIT 200 is from this point on governor limits from the workbook (http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_batch_interface.htm):

If no size is specified with the optional scope parameter of Database.executeBatch, Salesforce chunks the records returned by the start method into batches of 200, and then passes each batch to the execute method. Apex governor limits are reset for each execution of execute.
maylormaylor
Thanks @robdobby for the reply. To clarify my question, what is an example for creating a test class for batch apex? I've made the batch apex class work and tested it exactly the way you recommend by running the executeBatch method in an execute anonymous window. What I am trying to figure out is how to create a test class that tests my batch apex class to the 75% threshold. Note that my batch class is spoofing a reporting snapshot and basically runs a report and inserts it into the target report object.

Has anyone created a report snapshot capability via batch apex and knows how to write a test class for this scenario?
robdobbyrobdobby
This will execute the batch as a unit test.  Is that what you mean?
@isTest
private class Test_Snapshot {

	static testMethod void testBatchEscalationApproverUpdate() {

		// insert test data
		// ...

 		Test.startTest();
		
		SnapshotOpenCasesBatch sbatch = new SnapshotOpenCasesBatch();

		bact.isTest = true;
		
		Id batchprocessid = Database.executeBatch(sbatch, 1);
		
		Test.stopTest();

		System.assert(batchprocessid != null);
	}

}