• Kris Mariano
  • NEWBIE
  • 5 Points
  • Member since 2017

  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 0
    Questions
  • 1
    Replies

Nothing changed with my test - it only calls one batch job.  This worked 2wks ago, but now is failing, preventing me from uploading an important bug fix to my customers.

 

22:56:34.835|EXCEPTION_THROWN|[EXTERNAL]|System.UnexpectedException: No more than one executeBatch can be called from within a testmethod. Please make sure the iterable returned from your start method matches the batch size, resulting in one executeBatch invocation.

 

My only change was an addition of a 3rd test that calls the same method "TryBatchJobsAgain", which passes, despite this one failing.  Perhaps the test doesn't truly finish the batch job before the other test starts?

 

When I comment out this test, an unrelated test fails that calls a separate batch job.

 

When I move that test to a completely separate test class, it still fails.  

 

Test Method:

 

public static testMethod void testRemoveDelayBatchJobFromQueue() {
        Long numRecordsToCreate=2;
        Long numUsersToCreate=2;
        String profileName='System Administrator';

        Boolean Active=True;
        Double URDaysDelay=2;
        String URObjectName='UnfollowTest__c';
        String URFieldName='String__c';
        String UROperator='equals';
        String URValue='a';//testing capitalization as well
        List<UnfollowRule__c> urs=new List<UnfollowRule__c>();


        String Str='a';
        Boolean Check=TRUE;
        String Pick='2';
        Double Dec=1000;
        Date Dat=date.today();
        DateTime DatTim=dateTime.now();
        String Phone = '415-555-5555';
        String Email = 'test@test.com';
        String Url='www.test.com';
          
        cleanUpTestData();
        
        urs.add(createUR(Active, URObjectName, URFieldName, UROperator, URValue, URDaysDelay ));    
        insert urs;
        
        List<Id> recordIds=createUnfollowTestRecords(numRecordsToCreate,Check, Dat, DatTim, Dec, Email, Phone, Pick, Str, Url);
        List<User> users=createUsers(numUsersToCreate, profileName);
        List<EntitySubscription> subs=createSubs(users,recordIds);
        List<UnfollowQueue__c> uqs=new List<UnfollowQueue__c>();
        for (Id i:recordIds){
            UnfollowQueue__c uq=new UnfollowQueue__c();
            uq.recordId__c=i;
            uq.CriteriaMetDate__c=Date.Today()-URDaysDelay.intValue();
            uq.DaysDelay__c=URDaysDelay;
            uqs.add(uq);
        }//for 1

        List<Id> recordIds2=createUnfollowTestRecords(numRecordsToCreate,Check, Dat, DatTim, Dec, Email, Phone, Pick, Str, Url);
        List<EntitySubscription> subs2=createSubs(users,recordIds2);

        for (Id i:recordIds2){
            UnfollowQueue__c uq=new UnfollowQueue__c();
            uq.recordId__c=i;
            uq.CriteriaMetDate__c=Date.Today()-URDaysDelay.intValue()+1;
            uq.DaysDelay__c=URDaysDelay;
            uqs.add(uq);
        }//for 1

        insert uqs;
        
        Boolean delayJob = TRUE;
        Boolean delayRulesIncluded=FALSE;
        Boolean evalateEachRecordForDaysDelay=FALSE;
        Boolean addFieldNames=TRUE;
        String sObjectQuery = 'Select Id, chttrunfollow__recordId__c FROM chttrunfollow__UnfollowQueue__c WHERE chttrunfollow__scheduledUnfollowDate__c<= TODAY AND IsDeleted=FALSE';
        UnfollowBatchJobsQueue__c job=new UnfollowBatchJobsQueue__c(delayJob__c=delayJob, delayRulesIncluded__c=delayRulesIncluded, evalateEachRecordForDaysDelay__c=evalateEachRecordForDaysDelay, objectName__c=URobjectName, numRulesUsedInThisObject__c=urs.size(), sObjectQuery__c=sObjectQuery);
        insert job;
        
        system.debug('Unfollow Queue Size: '+[SELECT Id FROM UnfollowBatchJobsQueue__c].size());
        
        test.StartTest();
            unfollowTryBatchJobsAgain.unfollowTryBatchJobsAgain();//This is a regular class that calls 1 batch job        test.stopTest();
        
        //first confirm the job was removed from the queue
        List<UnfollowBatchJobsQueue__c> bjQueue = [SELECT Id FROM UnfollowBatchJobsQueue__c];
        system.AssertEquals(0,bjQueue.size());
        //then confirm it actually worked
        List<EntitySubscription> es=[Select Id FROM EntitySubscription WHERE ParentId IN :recordIds];
        system.assertEquals(0,es.size());
        uqs=[SELECT ID FROM UnfollowQueue__c WHERE ScheduledUnfollowDate__c<=TODAY];
        system.assertEquals(0,uqs.size());
        es=[Select Id FROM EntitySubscription WHERE ParentId IN:recordIds2];
        system.assertEquals(numRecordsToCreate*numUsersToCreate,es.size());
    }//testRemoveDelayBatchJobFromQueue

 

 

Note the highlighted red below is the ONLY batch job executed from this test - even the system logs confirm this.

 

global with sharing class unfollowTryBatchJobsAgain{

    public static void unfollowTryBatchJobsAgain(){
        Integer numBatchApexJobsLimit=5;//at time of coding, there are at most 5 concurrent batch apex jobs in any org
        List<AsyncApexJob> numBatchJobs = [SELECT Id, Status FROM AsyncApexJob WHERE Status = 'Queued' OR Status = 'Processing'];

        //This is the number of jobs that can be queued up by this method
        Integer numJobsAvailable=numBatchApexJobsLimit - numBatchJobs.size();
	
        if(numJobsAvailable>0){
            List<UnfollowBatchJobsQueue__c> batchJobsQueued=[SELECT Id, IsDeleted, delayJob__c, delayRulesIncluded__c, evalateEachRecordForDaysDelay__c, numRulesUsedInThisObject__c, objectName__c, sObjectQuery__c FROM UnfollowBatchJobsQueue__c WHERE IsDeleted=FALSE ORDER BY  CreatedDate ASC];
            //Goal here is to process the delay queue first as it's more important than the others. Rather than do 2 queries, it's handled with variables here:
            Integer delayJobNum=1000;//initialize to huge number as a backup
            for (Integer i=0;i<batchJobsQueued.size();i++){
                if (batchJobsQueued[i].delayJob__c==TRUE){
                    delayJobNum=i;
                    break;
                }//if 2
            }//for 1
    		List<UnfollowBatchJobsQueue__c> batchJobsToDelete=new List<UnfollowBatchJobsQueue__c>();        
            for(Integer i=0; i<numJobsAvailable && i<batchJobsQueued.size(); i++){
                //if this is the high priority "delayed records scheduled for unfollow today" job, do it first
                if (delayJobNum!=1000){
                    UnfollowProcessUnfollowQueueBatch unfollowDelayedRecords= new UnfollowProcessUnfollowQueueBatch();
                    unfollowDelayedRecords.sObjectQuery=batchJobsQueued[delayJobNum].sObjectQuery__c;                    try{
                        Id unfollowRulesProcessId = Database.executeBatch(unfollowDelayedRecords, 200); 
                        batchJobsToDelete.add( batchJobsQueued[delayJobNum]);
                    } catch(exception e){
//                        system.debug('Either the batch failed or the job deletion from teh queue failed: '+e);
                    }//try
                } else if(batchJobsQueued[i].delayRulesIncluded__c==FALSE){
                 //is this the simple case with no "days delay" rules?
                    UnfollowRecordsBatch  unfollowRecords= new UnfollowRecordsBatch();
                    unfollowRecords.ObjectName=batchJobsQueued[i].objectName__c;
                    unfollowRecords.numRulesUsedInThisObject=batchJobsQueued[i].numRulesUsedInThisObject__c.intValue();
                    unfollowRecords.sObjectQuery =  batchJobsQueued[i].sObjectQuery__c;
                
                    try{
                        Id unfollowRulesProcessId = Database.executeBatch(unfollowRecords, 200); 
                        batchJobsToDelete.add(batchJobsQueued[i]);
                    } catch(exception e){
//                        system.debug('Either the batch failed or the job deletion from the queue failed: '+e);
                    }//try
                } else {
                //else it's the more complex case where we need to check for the unfollow date
                    UnfollowQueueDelayRecordsBatch queueDelayRecords= new UnfollowQueueDelayRecordsBatch();
                    queueDelayRecords.ObjectName=batchJobsQueued[i].objectName__c;
                    queueDelayRecords.sObjectQuery =  batchJobsQueued[i].sObjectQuery__c;
                    queueDelayRecords.evalateEachRecordForDaysDelay=batchJobsQueued[i].evalateEachRecordForDaysDelay__c;
                    
//let's cross our fingers that the rule criteria didn't change between when this job first ran and now :(  
//Will the code fail elegantly if the rules were changed?
//I'd rather not create a 3rd queue just to save the state of the rules due to stupid batch apex limits
                    queueDelayRecords.delayRules=[Select Id, ObjectName__c, Active__c, FieldName__c, FieldType__c, Operator__c, Value__c, DaysDelay__c FROM UnfollowRule__c WHERE DaysDelay__c>0 AND Active__c = TRUE AND objectName__c=:queueDelayRecords.ObjectName]; 

                    try{
                        Id unfollowRulesProcessId = Database.executeBatch(queueDelayRecords, 200); 
                        batchJobsToDelete.add( batchJobsQueued[i]);
                    } catch(exception e){
//                        system.debug('Either the batch failed or the job deletion from the queue failed: '+e);
                    }//try
                    
                }//if 2
            }//for 1
            try{
            	delete batchJobsToDelete;
            } catch(exception e){
//                        system.debug('job deletion from the queue failed: '+e);            
            }//try delete

        }//if 1


    }//unfollowTryBatchJobsAgain
}//unfollowTryBatchJobsAgain

 

: