[SalesForce] Error: No more than one executeBatch can be called from within a testmethod

I have one batch class which has almost 6k records.I have written test class for the same.But i am getting error saying

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.

I am creating one record for which i wanted test class to run.But i can see in background org data is being running for batch jobs. I dont see batch job is not running for only one record which i inserted in test class.

Do I need to create a constructor which can have one parameter as user and then I can pass one user record inside test class.I am not sure why it is running batch jobs for all org data.Below is my batch class:

Batch class:

global Lighting_DeactivateUserBatch()
{
    this(getFrozenUserIds());
}
@TestVisible Lighting_DeactivateUserBatch(Set<Id> frozenUserIds)
{
    this.frozenUserIds = frozenUserIds;
}
public static Set<Id> getFrozenUserIds()
{
    List<UserLogin> frozenUserList;
    List<ID> frozenIDList = new List<ID>();
    Set<ID> setRecordOwner = new set<ID>();
    // generate collection
    frozenUserList = [Select UserID from UserLogin where isFrozen=true];
    for(userlogin frozen : frozenUserList) {
        frozenIDList.add(frozen.UserID);
    }
    AggregateResult[] ownerListOpp = [SELECT OwnerID Owner FROM Opportunity group by OwnerID];
    AggregateResult[] ownerListAcc = [SELECT OwnerID Owner FROM Account group by OwnerID];
    AggregateResult[] ownerListCon = [SELECT OwnerID Owner FROM Contact group by OwnerID];
    AggregateResult[] ownerListLead = [SELECT OwnerID Owner FROM Lead group by OwnerID];
    ownerListOpp.addall(ownerListACC);
    ownerListOpp.addall(ownerListCon);
    ownerListOpp.addall(ownerListLead);
    for(AggregateResult agg : ownerListOpp) {
        setRecordOwner.add((ID) agg.get('Owner'));
    }
    return setRecordOwner;
}

global List<User> start(Database.BatchableContext BC){

    //user u = new user(ID='00524000001IFILAA4');
    //List<user> queryForEmailAlert = [Select ID, Email, LastLoginDate ,createddate,Reactivation_Date__c,isportalenabled,TEST_CREATED_DATE__C,TEST_LOGIN_DATE__C from User where Functional_System_User__c=false and isActive =true and ID=:U.Id AND ID not in :frozenIDList ORDER BY TEST_LOGIN_DATE__C ASC NULLS LAST];
    List<user> queryForEmailAlert = [Select ID, Email, LastLoginDate ,createddate,Reactivation_Date__c,isportalenabled,TEST_CREATED_DATE__C,TEST_LOGIN_DATE__C from User where Functional_System_User__c=false and isActive =true AND ID not in :frozenIDList ORDER BY TEST_LOGIN_DATE__C ASC NULLS LAST];
    System.debug('users applicable for batch deactivation ::::: '+queryForEmailAlert);
    return (queryForEmailAlert);
    //return (u);
}

global void execute(Database.BatchableContext BC, List<sObject> scope){
    Set<ID> userOwnerSet = new Set<ID>();
    Set<ID> userDeactSet = new Set<ID>();
    List<User> listAllUser = (List<User>) scope;
 .....

Test class:

      @isTest
public class Lighting_DeactivateUserBatch_Test {

    public static testmethod void testScenario3() {

    Profile p = [SELECT Id FROM Profile WHERE Name='Standard User'];
    User u1 = new User(Alias = 'standt1',Country='United Kingdom',Code_1_ID__c='123abc45',Email='demo1@randomdemodomain.com',Functional_System_User__c=True, EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US',LocaleSidKey='en_US',ProfileId = p.Id,TimeZoneSidKey='America/Los_Angeles', UserName='demo1@andomdemodomain.com');
    insert u1;
    system.debug('User1@@@@@@'+ u1);
    Datetime newUserAlertDate = Datetime.now().addDays(-23);
    System.debug('value of new date ::::: '+newUserAlertDate);
    Test.setCreatedDate(u1.Id, newUserAlertDate);
    User u = [select id,Name,createddate,Functional_System_User__c,lastlogindate,isActive from user where id=:u1.id];
    MO_Inactive_Users__c dateParam = new MO_Inactive_Users__c(Name='Date Param',Grace_Period__c=5,Inactivation_Days_to_Deactivate__c=90,Inactivation_Days_to_Deactivate_New_User__c=30,Inactivation_Days_to_Notify__c=83,Inactivation_days_to_Notify_New_User__c=23);

        insert dateParam;
        System.debug('value of test user ::::: '+u);
        Test.startTest();
        Set<Id> testBlacklist = new Set<Id>();
        for (User user : [SELECT Id FROM User WHERE IsActive = true AND Functional_System_User__c = false]){
        testBlacklist.add(user.Id);

        }
        system.debug('testBlacklist-isFrozen@@@@@'+ testBlacklist); 

            Database.executeBatch(new Lighting_DeactivateUserBatch(testBlacklist));
        Test.stopTest();
    //userlogin US = Select id,isfrozen from userlogin where userid=u1.id;
    //system.debug('User1-isFrozen@@@@@'+ US.IsFrozen)//
    //User u2 = new User(Alias = 'standt2',Country='United Kingdom',Code_1_ID__c='6789xyz0',Email='demo2@randomdemodomain.com',EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US',LocaleSidKey='en_US', ProfileId = p.Id,TimeZoneSidKey='America/Los_Angeles', UserName='demo2@andomdemodomain.com');
    //insert u2;    
    //system.debug('User2@@@@@@'+ u2);
    }

}

Best Answer

You can decouple the data fairly simply in this case with a @TestVisible constructor:

global MyBatch()
{
    this(getFrozenUserIds());
}
@TestVisible MyBatch(Set<Id> frozenUserIds)
{
    this.frozenUserIds = frozenUserIds;
}
public static Set<Id> getFrozenUserIds()
{
    // generate collection
}

Now in your test, when you want to execute the batch, construct it with your own Set<Id>:

Set<Id> testBlacklist = new Set<Id>();
for (User user : [
    SELECT Id FROM User WHERE IsActive = true
    AND Functional_System_User__c = false
]) testBlacklist.add(user.Id);

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

// assert stuff

Note that you can still test the empty constructor as well, basically making sure it is getting the correct blacklist when you do not specify one. Since this list is quite large, as you have found, you will not be able to run the batch with it.