3

I have batchable apex that is throwing the Too Many Query Locator Rows error:

Global Class FocusDomainBatch implements Database.Batchable<sObject>, Database.allowsCallouts
    {
        global Database.querylocator start(Database.BatchableContext BC)
        {
            String query = 'Select id,name from Domain_Stats__c';
            return Database.getQueryLocator(query);    
        }


global void execute(Database.BatchableContext BC, List<sObject> scope)
    {
        try
        {
           List <Focus_Domain_Stats__c> allDomains = scope;          
           List <string> web = new List <string>();

There are more than 10k Domain_Stats__c records, but I was under the impression that in batchable that was ok?

It is being called by :

Public Class FocusDomainStatsSchedulable implements Schedulable 
{
    Public void execute(SchedulableContext SC)
    {
        try
        {
           if(!Test.isRunningTest())
           {
                FocusDomainBatch fd = new FocusDomainBatch();
                Database.executeBatch(fd,5);
           }

         } 
         Catch(exception e)
         {
             throw e;
         }
    }
}

Here is the test class that is failing:

@IsTest(seealldata=true)

    Public Class TestFocusDomainBatch
    {

    Static testMethod void TestFocusDomainBatch ()
    {               

        Test.StartTest();

        Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator1());
        FocusDomainBatch fb = new FocusDomainBatch(); 
        Date lastMonth = Date.Today().addMonths(-6);
        fb.start(null); //Failing here
        fb.finish(null);
        //List<Focus_Product__c> fpContacts = [select id,contact__c from Focus_Product__c where createddate > :lastmonth and contact__C <> null limit 1];
        //List<Focus_Product__c> fpLeads = [select id,contact__c from Focus_Product__c where createddate > :lastmonth and contact__C <> null limit 1];        

        List <Lead> allLeads = [Select Id, name, HG_Focus_Sign_Up_Date__c, website from Lead where createddate > :lastMonth and isConverted = false and Focus_Sign_Up_Date__c <> null limit 1];
        List <Contact> allContacts = [Select Id, name, HG_Focus_Sign_Up_Date__c, account.website from contact where createddate > :lastMonth and Focus_Sign_Up_Date__c <> null limit 1]; 
        List<string> webs = new List<string>();
        if(allLeads.size() > 0)
            webs.add(allLeads[0].webSite);
        if(allContacts.size() > 0)
            webs.add(allContacts[0].account.webSite);
        //database.executebatch(fb);
        fb.execute(null,[Select id,name, First_user_sign_up_date__c, Total_Users__c, New_Users_In_Last_Month__c, Avg_Fvrt_Pro_Per_User__c, Users_with_zero_fvrts__c, Users_with_2_or_less_fvrts__c, Average_daily_queries__c, Average_monthly_queries__c, Total_current_month_queries__c, Total_Queries__c from Focus_Domain_Stats__c where name in :webs]);
        Test.StopTest();

    }
  • 2
    yes but upper limit is 50 million. so you should check too. Also you are sure you are getting error here? – Tushar Sharma May 18 '16 at 16:40
  • 2
    You must re-query for them somewhere else. The code you have posted should give you no issue. – Adrian Larson May 18 '16 at 16:42
  • @AdrianLarson I updated the question to show more detail. – Justin May 18 '16 at 16:51
  • Doesn't have anything to do with the solution but you should remove your Test.isRunningTest check. Never use it if you can avoid it! – Adrian Larson May 18 '16 at 16:52
  • updated to show the test class. It is failing where it says fb.start(null) – Justin May 18 '16 at 16:58
4

The way you should really test a batch is by calling Database.executeBatch. Don't call start and execute directly.

You should also test without SeeAllData. You should never use SeeAllData unless it's your only choice. There are many reasons why it's bad practice to unit test against existing data. Unit tests will not perform the same in your sandboxes as your staging/production environments. Your tests will run significantly slower. You have a lot less control over what you're actually testing.

Take a look at Using Batch Apex (specifically the Testing Batch Apex section). You need to make sure you get at most one execute block, which effectively limits you to 200 records.

static testMethod void testMyBatch()
{
    // setup data for your batch to process
    // make sure it's less than 200 records
    // you can only call the execute method once!

    Test.startTest();
        Database.executeBatch(new MyBatch());
    Test.stopTest();

    // assert on results
}
| improve this answer | |
  • Thanks, I had removed the focus part before (was trying to clean up). – Justin May 18 '16 at 17:07
  • @Justin I'm surprised I can't find a better resource for why data silos are so important in unit testing for Apex...but you should definitely start by removing SeeAllData and trying to create your own data for the test. – Adrian Larson May 18 '16 at 17:28
  • Thanks, Ill give that a shot. In this case, I think it was because the class uses a callout that pulls in login credentials from a custom setting, so it is doing a SOQL to get those. – Justin May 18 '16 at 17:55
  • 1
    I don't recall where I read about SeeAllData = false as best practice, but the reason is simple: relying on production data can cause your unit tests to fail arbitrarily simply because a record was created, updated, or deleted. It makes for very fragile tests. – sfdcfox May 18 '16 at 18:27
  • 1
    there are some occasions where the start() and execute() must be called individually (or at least when I blogged about it). – cropredy May 18 '16 at 21:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.