+ Start a Discussion
TehNrdTehNrd 

test methods for Batch Apex is not causing execute() method to run

I have a very simple Batch class and I am trying to write a unit test to cover it but the execute method in this batch class is never executing. I'm stumped.

 

Batch Class:

global class ideaCleanBatch implements Database.Batchable<sObject>{

global Database.QueryLocator start(Database.BatchableContext bc){
//We want to process all Ideas
return Database.getQueryLocator('select Id from Idea');
}

global void execute(Database.BatchableContext bc, List<sObject> objects){
Set<Id> ideaIds = new Set<Id>();
for(sObject s : objects){
Idea i = (Idea)s;
ideaIds.add(i.Id);
}
//Send ideas to ideaClean for processing
ideaClean.recalcNumbers(ideaIds);
}

global void finish(Database.BatchableContext bc){
system.debug('All done.');
}
}

Test Method:

static testMethod void ideaBatchTest(){
List<Idea> ideas = new List<Idea>();
Id communityId = [select Id from Community limit 1].Id;
for(Integer i = 0; i < 200; i++){
ideas.add(new Idea(Title = 'myIdea' + i, CommunityId = communityId));
}
insert ideas;

Test.startTest();
ideaCleanBatch job = new ideaCleanBatch();
ID batchprocessid = Database.executeBatch(job);
Test.stopTest();
}

Coverage:

 

Thanks,

Jason

 

4larryj4larryj

We have the exact same problem.  Hopefully there will be a fix soon!  

 

Commenting out assert statements in a test method is not a best practice :).  But it's what we're forced to do now because of this issue.

TehNrdTehNrd
I've got an open case but no news yet.
TehNrdTehNrd

I finally got an answer...

 

Apparently the test context for batch apex only allows for 200 records. Anything over that limit will not execute, at all.


One typical way of limiting the number of records is to have a string as a member variable that stores the query. The test method then puts a limit on the query of 200 records. For example, the following code achieves 100% coverage:

global class BatchClass implements Database.Batchable<sObject>{

public String query = 'select id from account';

global Database.QueryLocator start(Database.BatchableContext BC){
return Database.getQueryLocator(query);
}

global void execute(Database.BatchableContext BC, List<sObject> scope){
for (Account a : (List<Account>)scope){
System.debug(a);
}
}

global void finish(Database.BatchableContext BC){
System.debug('finished');
}
}

 

@isTest
private class TestBatchClass{
static testMethod void testBatchClass() {
BatchClass bc = new BatchClass();
bc.query = 'Select Id From Account Limit 200';

Test.startTest();
Database.executeBatch(bc, 200);
Test.stopTest();
}
}

I think this is sort of lame as I feel like the code should just know this is a test and only return 200 records so one batch is executed.

This is what the docs say:

The testing framework allows developers to test one execution of the executeBatch method.To guarantee your test runs within the governor limits, add LIMIT 200 to the query.

I easily misinterpreted this as it will only exexcute one batch regardless of the amount returned and the limit 200 is just to make the code more optimized and was not required.

-Jason

Message Edited by TehNrd on 11-10-2009 12:12 PM
Message Edited by TehNrd on 11-10-2009 12:15 PM
LegerdemainLegerdemain

Reference to docs: http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_batch_interface.htm

under testing batch apex...

I agree with you that this is easy to miss: 

"To guarantee your test runs within the governor limits, add LIMIT 200

to the query"

 

Larry

LegerdemainLegerdemain

Although, in defense of the doc writers, it is easier to understand when looking at the comments in the sample code:

// Important - the Salesforce.com test framework only allows you to  

// test one execute.  

// Therefore, developers must construct their query to  

// only return 200 records 
TehNrdTehNrd
The thing that is confusing is that it allows you to test one and only one. This is the part that wasn't clear to me. I assumed if the query returned 400 records only one batch would execute. In actuality, it fails completely, also with no type of notification as to why.
Ron WildRon Wild

It's probably so that in the context of a test, they can ensure that the batch method will execute immediately and completely, allowing the test method to complete and execute System.asserts on the results.

 

If the batch had to reschedule to complete it's work, salesforce would have to figure out how to remove the delay between runs and force the subsequent jobs to run immediately.

 

Ron

OldDeadBugOldDeadBug

I've tried the suggestions in this thread, and I still can't get the execute to test. Now, I'm having to work with someone else's code unfortunately and I have the impression it wasn't written correctly.

For example the query was actually built inside the start method, rather than outside the method as a class variable - this prevented any ability for me to use the LIMIT 200 in the test query.

 

I've now added the query as a class string variable, and am updating the query to include LIMIT 200 in the test before it enters the start method.

 

Also, the debug logs show the query is returning the test records I created. I'll try to simplify the code:

 

 

global class batchJob implements Database.Batchable<SObject>, Database.Stateful 
{
    global List<String> emailIdsList; 
    global List<Id> emailTargetIds;

    public BatchApex_PriceExceptionFieldUpdates()
    {
        emailIdsList = new list<String>();
        emailTargetIds = new List<Id>();
    }

    
    String str_SOQL = 'select email from Contact';

    global Database.QueryLocator start(Database.BatchableContext BC) 
    {
        return Database.getQueryLocator(str_SOQL);
    }

    global void execute(Database.BatchableContext BC, List<SObject> sObjects) 
    {
        list<Contact> Contacts = new list<Contact>();
        for (Sobject SO : sObjects)
        {
             Contact C = (Contact) SO;
             .... more code
        }
    }

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

 

 

Here's the test method:

 

 

static testMethod void invokeTestMethod()
{
    ... set up test records
    test.startTest();
    batchJob Batch = new batchJob();
    
    Batch.str_SOQL += 'LIMIT 200';
    Id batchprocessId = Database.executeBatch(Batch, 200);
}

 

 

Again, the query is returning records inserted by the testmethod, and the start method is working. The test however never runs the execute method.

 

So, given that the 200 record limit is being imposed on the batch query in the test, the query is returning records, and thre are no other errors in the debug log, what am I doing or not doing?

 

The job is more complex than this as far as the internal code for the execute and finish methods, but since neither of them are being called, this isn't impacting the result. However, if it will help I'll add the rest. Otherwise, the process seems to be working in practice, but I can't improve the test results until I can get the rest of the methods to run.

Susana NTSSusana NTS

OldDeadBug, were you able to solve the problem?

I'm stuck in the same place as you.

 

I just insert 8 leads in a testmethod for the bacthable class to process but the execute method does not execute nor leave any trace in the debug log.

 

Any clue on this?

 

Thanks

Susana NTSSusana NTS

Has this anything to do with the type of sandbox I'm using?

I does not work on a Developer sandbox but it works find on a Config Only sandbox.

OldDeadBugOldDeadBug

Nothing yet. The only 'solution' provided is to ensure your test batch is limited to 200 records. I've done that in my query, as displayed above. The start method runs just fine, but the execute doesn't run at all, and there is no indication of what the error is.

 

I thought the whole idea of this process is that all three methods get called if there aren't any obvious errors. But currently the test process isn't set up to effectively test batch jobs, or at least to return some kind of error message when the test doesn't complete.

 

I guess the Salesforce people assume that if the execute method doesn't run in the test that its a batch size problem. I don't think that is the problem in my case. Its probably something simple but so far no one has pointed it out.

OldDeadBugOldDeadBug

 


TehNrd wrote:

I finally got an answer...

 

Apparently the test context for batch apex only allows for 200 records. Anything over that limit will not execute, at all.


One typical way of limiting the number of records is to have a string as a member variable that stores the query. The test method then puts a limit on the query of 200 records. For example, the following code achieves 100% coverage:

global class BatchClass implements Database.Batchable<sObject>{

public String query = 'select id from account';

global Database.QueryLocator start(Database.BatchableContext BC){
return Database.getQueryLocator(query);
}

global void execute(Database.BatchableContext BC, List<sObject> scope){
for (Account a : (List<Account>)scope){
System.debug(a);
}
}

global void finish(Database.BatchableContext BC){
System.debug('finished');
}
}

 

@isTest
private class TestBatchClass{
static testMethod void testBatchClass() {
BatchClass bc = new BatchClass();
bc.query = 'Select Id From Account Limit 200';

Test.startTest();
Database.executeBatch(bc, 200);
Test.stopTest();
}
}

I think this is sort of lame as I feel like the code should just know this is a test and only return 200 records so one batch is executed.

This is what the docs say:

The testing framework allows developers to test one execution of the executeBatch method.To guarantee your test runs within the governor limits, add LIMIT 200 to the query.

I easily misinterpreted this as it will only exexcute one batch regardless of the amount returned and the limit 200 is just to make the code more optimized and was not required.

-Jason

Message Edited by TehNrd on 11-10-2009 12:12 PM
Message Edited by TehNrd on 11-10-2009 12:15 PM

 

I added this Batchclass and its test method to see if this actually worked. This has been flagged as a solution, but after running the test I get the same problem:

 

The start method runs, the SOQL query returns a result under 200 records, the execute method is bypassed altogether, and then the finish method runs.

 

The 200 record limit on the test is necessary, but this is not the reason for the batch execute method not running.

 

 

LinvioIncLinvioInc

The Test class now supports the method Test.isRunningTest() which you can use to determine whether or not you are in apex test mode ... and insert your "LIMIT 200" clause into the SOQL.

 

- Ron

 

OldDeadBugOldDeadBug

 


LinvioInc wrote:

The Test class now supports the method Test.isRunningTest() which you can use to determine whether or not you are in apex test mode ... and insert your "LIMIT 200" clause into the SOQL.

 

- Ron

 


 

Appreciate the response, but I'm not clear on where to use the test.isRunningTest() method in this case.

 

At which point in the batch class do I need to inform it that a test is running??

 

Also, I'm updating the SOQL query to insert the 'LIMIT 200' in the test method before running the execution.

 

Since the execute method is the only one not firing, are you suggesting I add the isRunningtest method before the execute method, such as in the start method??

 

Not sure what you're suggesting here, nor how it will impact the problem of the execute piece of the batch process not running. Any help is appreciated by myself and others.

 

 

OldDeadBugOldDeadBug

As far as testing a batch process is concerned I believe I've found the best workaround to get the batch class to test successfully.

 

Since even a very simple batch class and test such as that provided earlier in this thread by TehNerd doesn't run the 'execute' method, regardless of whether the LIMIT 200 is added to the QueryLocator query, I chose to add a separate method to the batch class called 'execbatch()' which is referenced from the 'execute' method. Basically it looks like this:

 

 

global class BatchProcess implements Database.Batchable<sObject>, database stateful
{

global void execute(Database.BatchableContext BC, List<SObject> scope) { list<Account> list_updateSFDC = execbatch((list<Account>) scope);
}

public list<Account> execbatch(list<Account> AcctList)
{
... move logic from the execute method into here where it can be tested
outside the batch process that requries a database.batchableContext.
}
}

in the test method:
static testMethod void testBatch()
{
... set up test objects
list<Account> Accts = ... get test object records into a list that mimics the QueryLocator in the start method

BatchProcess BP = new BatchProcess();
Accts = BP.execbatch(Accts); //... this tests the processing that would preferably be in the execute method

test.startTest();
Database.executebatch(BP,200);
test.stopTest();
}
}


.. this will run the standard batch methods start, execute, and finish - but since the execute method doesn't run during the test, this will be missed. So although you won't get 100%, the additional execbatch() method will get coverage of what should be the execute method logic.

If anyone can provide a working batch class and test method that can demonstrably test the execute method, I really want to see it. I've been unable to test the execute on even the simplest of batch classes. It might be a really simple error I'm making but I've so far been unable to find it. This workaround will get your code tested while keeping the batch processing intact.

 

 

LinvioIncLinvioInc

That's a good approach.  

 

As another alternative, you can assign an alternate SOQL string to a public member variable on the batch class before running it - essentially changing the default query to one that is more restrictive and produces the smaller result set that you need for the tests.

 

 

   BatchProcess BP = new BatchProcess();
   
   BP.soql = 'Select Id, Name .... from X where ...... Limit 100';

   test.startTest();
   Database.executebatch(BP,200); 
   test.stopTest();

 

 

 

Cheers,

Ron

 

SFBenSFBen

I think something has changed recently affecting batch apex.  I ran tests at the start of March and got 99% coverage but ran today and got 23% as it wasn't running the execute method.  Tried to use the limit 200 technique but this didn't do a thing.  Had to call the execute method seperately from everything else:

 

BatchClass batch = new BatchClass(record);
Test.startTest();
Database.executeBatch(batch, 200);
batch.execute(null, entries);
Test.stopTest();

 

ministe2003ministe2003

Same problem here, glad it's not just me,

Just wrote a new batch class and copied the test code from another batch test I'd written months previous, which I know for sure got me 100% coverage (in fact, I'd even wrote in comments that it got me 100%!).  Now the start and finish methods are run, but execute isn't even called.

 

Regards

Steven

MycodexMycodex

bah, any resolution to this issue? My execute method is being bypassed.

ministe2003ministe2003

Do some debugging to make sure your query is actually returning results, if it doesnt return results your execute method wont be called at all.  Limit the query to 200 results too.

MycodexMycodex

Spot-on. My class was querying another table that happened to be empty in the sandbox. Lesson learned: no data = no execute

 

I also decided to pull the query statement out of the batch class so I could dynamically create it. Here is how I called it from my schedulable class

 

 

reviewPASrecords rpr = new reviewPASrecords();
rpr.query='SELECT Id FROM Producer_Account_Summary__c WHERE Account__c = null';
id batchinstanceid = database.executeBatch(rpr); 

 

 

and here is how I ran it from my test class to include the limit of 200

 

 

reviewPASrecords rpr = new reviewPASrecords();
rpr.query='SELECT Id FROM Producer_Account_Summary__c WHERE Account__c = null LIMIT 200';
Database.executeBatch(rpr); 

 

 

kevoharakevohara

I am a firm believer that this is a serious bug...

 

I have an org where I have two test methods in separate classes that call the same batch, same query, and same query results.  One works and calls the execute, the other fails.  This is a major problem as we are heavy batch users.

 

I have desparately tried to figure out what I need to do to trick the system into running the entire batch.  Nothing.  

 

As a final test, I deployed the code to another dev org with the same config.  In that org, the batch works on both of the above methods.

 

 

Sunny83Sunny83

Seems that this is happening only in test environment. I tried this in development env and it worked perfectly fine. But when I copied the code to test, it bypassed the execute method.

James LoghryJames Loghry

This thread gave me an epiphany sort of moment with the same if not similar issue I was having.

 

I was returning an empty iterable<sObject> in my start method.  The brunt of the logic is in the execute method, since I have to query across multiple objects.  The code worked fine when called via a Tab / custom visualforce page (Requirement to manually kick off the batch process).  

 

During unit testing, as everyone else as mentioned, the execute method was not firing.

 

So in the start method, I just added a dummy sObject, without any SOQL or DML to fix the unit test failure.

 

 

Teach_me_howTeach_me_how

Its now happening to me. Please help me

doubleminusdoubleminus

Happening to me as well. The execute method is being bypassed for seemingly no reason, with no trace of why in log files.

LinvioIncLinvioInc

After seeing renewed activity on this thread, it occurred to me that some of the folks posting might be writing these batch scripts using the latest API (v 25).  Don't forget that unless you run with @isTest(UseExistingData=true) SF will clear out all data before running the test.  The query in your script might be returning 0 records when you are expecting data to be there.

 

http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_annotation_isTest.htm

 

~ Ron

RoyalRazRoyalRaz

Use @IsTest(SeeAllData=true)Annotation on the top your test class

sf dev 2013sf dev 2013

I was only getting 40% test coverage... but when I moved my "update" statement into the "global void finish(Database.BatchableContext BC)"...  finally I got 100% test coverage. 

Onur KayaOnur Kaya
Hi All,

After adding @isTest(SeeAllData=true) annotation on top of the test class everything worked fine with me. 

 
Sda SmnSda Smn
Hello everybody,

I am facing the same issue, the execute method in test class did not run.
I tried to put limit 200 and limit 1 but the error persist. Same for adding the @isTest(SeeAllData=true) annotation on top of the test class.
The query returns value also.

Any solution other than the solutions mentioned above.
Thank you in advance.
Regards,
Alessio Bortone 16Alessio Bortone 16
Guys, use the @isTest(SeeAllData=true) annotation and make sure your set of test data is covered in the execution (ie: if the conditions for executing are not met, the execute method doesn't run and provides no coverage). This solved the issue for me, across all my orgs.