0

I have this batch class.

global without sharing class TestBatch implements Database.Batchable<SObject>,database.stateful 
{


   List<Opportunity> oppList = new List<Opportunity>();
   List<OpportunityLineItem> OliList = new List<OpportunityLineItem>();

   global TestBatch (List<Opportunity> oppList,List<opportunitylineitem>OliList)
   {
        this.oppList  = oppList;
        this.OliList = OliList;

   }

    global Iterable<Sobject> start(Database.BatchableContext bc) 
    {
           return oppList;
    }
    global void execute(Database.BatchableContext bc,List<opportunity> scope)
    {

    }
    global void finish(Database.BatchableContext bc) 
    {


        AsyncApexJob a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed,TotalJobItems FROM AsyncApexJob WHERE Id = :BC.getJobId()];


        if(a.status == 'Completed' && (a.NumberOfErrors==null || a.NumberOfErrors==0))
        {
           //2 lines of code

        }else{
            //Some 50 lines of code
        }

    }

}

How can I make AsyncApexJob a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed,TotalJobItems FROM AsyncApexJob WHERE Id = :BC.getJobId()];

this query return Error in test class. Is there any way? If the query does not return any error then it enters the if and executes only two lines where as in else we are triggering email and doing some extra stuffs and the test coverage percentage decreases.

Apart from adding Test.isRunningTest() is there any other way of making the query return an error?

Execute code

Database.SaveResult[] results = Database.update(scope, true);
for (Integer i = 0; i < results.size(); i++) {

    if (results[i].isSuccess()) {

    System.debug('Successfully created ID: '

    + results[i].getId());

    }else{
    system.debug('Errors: '+ results[i].getErrors()[0].getMessage()) ;
}
}

1 Answer 1

3

Your execute method doesn't need to check success/failure like you've written; if a record fails to update, you'll get an exception, so the "failed" branch will never execute, although technically, this doesn't matter, because debug statements do not need coverage.

To force a failure in this case, however, you just need to inject a bad opportunity:

Opportunity[] oppList = new Opportunity[] {
  new Opportunity()
};
TestBatch b = new TestBatch(oppList, null);
Test.startTest();
Database.executeBatch(b);
Test.stopTest();

Without seeing your execute method, there's no way to tell for sure, but typical ways that you can cause errors include injecting invalid data into your batch class, such as providing a record with an Id but the code is expecting to insert, or removing the ID when the code is expecting to update. You can even make a fake ID and put it in one of your records to force a failure, etc. Of course, if you have really good error handling, you might not be able fake the data. In that case, consider putting your logic into a separate function you can test:

if(a.status == 'Completed' ...) {
  doSuccess(this);
} else {
  doFailure(this);
}
...
public static void doFailure(TestBatch b) {
  ...
}

You can then inject whatever data you need to, and at maximum, you'll just have one uncovered line of code instead of 50.

2
  • I have updated the execute method in my question, it just does a dummy update on Opportunity. And it does a database.update which I know wont make the batch fail Jun 15, 2018 at 15:21
  • @RahulJain I've edited my answer based on your edit.
    – sfdcfox
    Jun 15, 2018 at 15:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .