2

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='[email protected]',Functional_System_User__c=True, EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US',LocaleSidKey='en_US',ProfileId = p.Id,TimeZoneSidKey='America/Los_Angeles', UserName='[email protected]');
    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='[email protected]',EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US',LocaleSidKey='en_US', ProfileId = p.Id,TimeZoneSidKey='America/Los_Angeles', UserName='[email protected]');
    //insert u2;    
    //system.debug('User2@@@@@@'+ u2);
    }

}
6
  • How to restrict test class batch run for only one record which i am inserting in test class.
    – Jack vardy
    Jan 29, 2016 at 14:47
  • I can see in the logs i am getting org data while running test class
    – Jack vardy
    Jan 29, 2016 at 16:26
  • Make sure your API Version >= 24. Prior to that the default for SeeAllData was true. You could also instead of testMethod use @IsTest(SeeAllData=false)
    – Adrian Larson
    Jan 29, 2016 at 16:45
  • Robert.. few objects like userlogin, profiles records can be accessible in test class. What you can do modify your batch constructor ( create two constructor one for batch and another for test) and pass list of record in that constructor.. and use that record in further process
    – Ratan Paul
    Jan 29, 2016 at 16:56
  • Robert.. few objects like userlogin, profiles records can be accessible in test class. What you can do modify your batch constructor ( create two constructor one for batch and another for test) and pass list of record in that constructor.. and use that record in further process
    – Ratan Paul
    Jan 29, 2016 at 16:57

3 Answers 3

1

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.

0

In your test, you could modify all users in order to the query condition is not satisfied, then create your test data.

This should work, but maybe it is not the best solution.

8
  • Problem for me is all org data is getting into scope rather than test data which i am inserting.I thought in test class,only test data will be executed by batch class execute method.
    – Jack vardy
    Jan 29, 2016 at 16:33
  • Real data are not used in tests, excepting some of them. You can see the list here developer.salesforce.com/docs/atlas.en-us.apexcode.meta/… And yes, users and in test scope. Jan 29, 2016 at 16:36
  • So how to avoid this error as per my test class.What shall i improve ?
    – Jack vardy
    Jan 29, 2016 at 16:40
  • Add the next code before create your user (the code is not tested). List<UserLogin> userLoginList = [Select UserID, isFrozen from UserLogin where isFrozen=true]; for(UserLogin frozen : userLoginList) { frozen.isFrozen = false; } update userLoginList; Jan 29, 2016 at 16:45
  • Add the next code before create your user?
    – Jack vardy
    Jan 29, 2016 at 16:47
0

I got what was the root cause of issue.I was getting more than 6k org data from start method query.As per test class of batch,Batch must run only once as per governor limit.So now i have restricted no. of user records specific to test class by using Test.IsRunningTest() context.I have added limit of 100 specific for test class.

Global variable:
global list<user> queryForEmailAlert;

start() Method:-

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

        if(Test.isRunningTest()){
        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 limit 100];
        }else{
        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);

    }

You must log in to answer this question.

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