When testing your batch Apex, you can test only one execution of the execute method. You can use the scope parameter of the executeBatch method to limit the number of records passed into the execute method to ensure that you aren’t running into governor limits.
The executeBatch method starts an asynchronous process. This means that when you test batch Apex, you must make certain that the batch job is finished before testing against the results. Use the Test methods startTest and stopTest around the executeBatch method to ensure it finishes before continuing your test. All asynchronous calls made after the startTest method are collected by the system. When stopTest is executed, all asynchronous processes are run synchronously. If you don’t include the executeBatch method within the startTest and stopTest methods, the batch job executes at the end of your test method for Apex saved using Salesforce.com API version 25.0 and later, but not in earlier versions.
Starting with Apex saved using Salesforce.com API version 22.0, exceptions that occur during the execution of a batch Apex job that is invoked by a test method are now passed to the calling test method, and as a result, causes the test method to fail. If you want to handle exceptions in the test method, enclose the code in try and catch statements. You must place the catch block after the stopTest method. Note however that with Apex saved using Salesforce.com API version 21.0 and earlier, such exceptions don’t get passed to the test method and don’t cause test methods to fail.
Example:1
Apex Class:
global class BatchApexTestExample implements Database.Batchable<Sobject>{
global Database.QueryLocator start(Database.BatchableContext bc){
return Database.getQueryLocator('select leadsource,rating from lead');
}
global void execute(Database.BatchableContext bc,List<Lead> scope){
for(Lead l : scope){
if(l.leadsource=='web'){
l.rating='warm';
}
}
update scope;
}
public void finish(Database.BatchableContext bc){
}
}
Test Class
@isTest
private class BatchApexTestExampleTest {
static testMethod void testme(){
Lead l = new Lead();
l.FirstName='Salesforce';
l.LastName='Codes';
l.Company='salesforcecodes';
l.leadsource='Web';
insert l;
Test.startTest();
BatchApexTestExample ba= new BatchApexTestExample();
Id jobid= Database.executeBatch(ba,5);
Test.stopTest();
Lead leads = [select rating from lead where id=:l.Id];
system.assertEquals('Warm', leads.Rating);
}
}
Example: 2
Apex Class:
global class AccountUpdate implements Database.Batchable {
global Database.QueryLocator start(Database.BatchableContext BC){
return Database.getQueryLocator('Select Id, Name From Account');
}
global void execute(Database.BatchableContext BC, List scope){
for(Account a: scope){
a.Name += ' Updated';
}
update scope;
}
global void finish(Database.BatchableContext BC){}
Test Class:
@isTest
private class accListountUpdate {
static testmethod void test() {
// Create test accounts to be updated
// by the batch job.
Account[] accList = new List();
for (Integer i=0;i<200;i++) {
Account m = new Account(Name = 'Account ' + i);
accList.add(m);
}
insert accList;
Test.startTest();
AccountUpdate c = new AccountUpdate();
Database.executeBatch(c);
Test.stopTest();
// Verify accounts updated
Account[] accUpdatedList = [SELECT Id,Name FROM Account];
System.assert(accUpdatedList[0].Name.Contains('Updated'));
}
}
In below code, we are testing above batch class by creating 200 test accounts to make sure that Account Name gets updated by batch apex correctly.
As best practice, you must execute the batch class between Test.StartTest() and Test.StopTest().
We can also load data from static resource to create Accounts using the following syntax.
List ls = Test.loadData(Account.sObjectType, ‘myResource’);
You must store the static resource (e.g. account.csv) under Salesforce static resources. The supported types of files are text/csv, application/vnd.ms-excel, application/octet-stream, text/plain.
Example : 3
Apex Class:
The following sample class finds all account records that are passed in by the start() method using a QueryLocator and updates the associated contacts with their account’s mailing address. Finally, it sends off an email with the results of the bulk job and, since we are using Database.Stateful to track state, the number of records updated.
public class UpdateContactAddresses implements
Database.Batchable<sObject>, Database.Stateful {
// instance member to retain state across transactions
public Integer recordsProcessed = 0;
public Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator(
'SELECT ID, BillingStreet, BillingCity, BillingState, ' +
'BillingPostalCode, (SELECT ID, MailingStreet, MailingCity, ' +
'MailingState, MailingPostalCode FROM Contacts) FROM Account ' +
'Where BillingCountry = \'USA\''
);
}
public void execute(Database.BatchableContext bc, List<Account> scope){
// process each batch of records
List<Contact> contacts = new List<Contact>();
for (Account account : scope) {
for (Contact contact : account.contacts) {
contact.MailingStreet = account.BillingStreet;
contact.MailingCity = account.BillingCity;
contact.MailingState = account.BillingState;
contact.MailingPostalCode = account.BillingPostalCode;
// add contact to list to be updated
contacts.add(contact);
// increment the instance member counter
recordsProcessed = recordsProcessed + 1;
}
}
update contacts;
}
public void finish(Database.BatchableContext bc){
System.debug(recordsProcessed + ' records processed. Shazam!');
AsyncApexJob job = [SELECT Id, Status, NumberOfErrors,
JobItemsProcessed,
TotalJobItems, CreatedBy.Email
FROM AsyncApexJob
WHERE Id = :bc.getJobId()];
// call some utility to send email
EmailUtils.sendMessage(job, recordsProcessed);
}
}
The code should be fairly straightforward but can be a little abstract in reality. Here’s what’s going on in more detail:
- The start method provides the collection of all records that the execute method will process in individual batches. It returns the list of records to be processed by calling Database.getQueryLocator with a SOQL query. In this case we are simply querying for all Account records with a Billing Country of ‘USA’.
- Each batch of 200 records is passed in the second parameter of the execute method. The execute method sets each contact’s mailing address to the accounts’ billing address and increments recordsProcessed to track the number of records processed.
- When the job is complete, the finish method performs a query on the AsyncApexJob object (a table that lists information about batch jobs) to get the status of the job, the submitter’s email address, and some other information. It then sends a notification email to the job submitter that includes the job info and number of contacts updated.
Test Class:
@isTest
private class UpdateContactAddressesTest {
@testSetup
static void setup() {
List<Account> accounts = new List<Account>();
List<Contact> contacts = new List<Contact>();
// insert 10 accounts
for (Integer i=0;i<10;i++) {
accounts.add(new Account(name='Account '+i,
billingcity='New York', billingcountry='USA'));
}
insert accounts;
// find the account just inserted. add contact for each
for (Account account : [select id from account]) {
contacts.add(new Contact(firstname='first',
lastname='last', accountId=account.id));
}
insert contacts;
}
@isTest static void test() {
Test.startTest();
UpdateContactAddresses uca = new UpdateContactAddresses();
Id batchId = Database.executeBatch(uca);
Test.stopTest();
// after the testing stops, assert records were updated properly
System.assertEquals(10, [select count() from contact where MailingCity = 'New York']);
}
}
No comments:
Post a Comment