2

I have a scenario where a trigger should take certain actions based on the number of Contacts assigned to an Account: If a Contact has no account assigned it will look for a special "Unassigned" Account and assign this to the contact. But if an Account has a large number of Contacts already it should not be used.

To test this I need to create an account that has more than the limit of Contacts (in my case its 5000)

I've tried to create a simple batch class to support this:

public class BulkInsert implements Database.Batchable<sObject>{
    public List<sObject> objects;

    public BulkInsert ( List<sObject> objs){
        this.objects = objs;
    }
    public Iterable<sObject> start(Database.BatchableContext BC){
        return objects;
    }
    public void execute(Database.BatchableContext BC, List<sObject> scope){
        insert scope;
    }
    public void finish(Database.BatchableContext BC){
    }
}

This is used in the test as follows:

    account = new Account(Name='Unassigned');
    insert account;

    bucket_o_contacts = new List<Contact>();
    for (Integer i = 0; i < 5000; i++){
        c = new Contact(LastName='Contact' + i, Account=account);
        bucket_o_contacts.add(c);
    }

    Test.startTest();
    Database.executeBatch(new BulkInsert(bucket_o_contacts));
    Test.stopTest();

Why doesn't this work? Have I misunderstood how Batchable works?

This is the error I get:

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.

If I write some Anonymous Apex and execute it then the batch works:

Contact c;
Account account = new Account(Name='Unassigned');
insert account;
List<Contact> bucket_o_contacts = new List<Contact>();
for (Integer i = 0; i < 500; i++){
    c = new Contact(LastName='Contact' + i, Account=account);
    bucket_o_contacts.add(c);
}
Database.executeBatch(new BulkInsert(bucket_o_contacts));

Any ideas?

  • 2
    why not use an @TestVisible Integer maxContactsPerAccount static variable in your batch class that is initialized to 5000 by the PROD code but can be set to something much smaller in the testmethod - this way you can avoid creating 5000 Contacts and still test your logic with only, say, 2 Contacts per Account – cropredy Jan 8 '15 at 18:04
  • Yes @crop1645, parametising the code to work below the batch threshold is a good idea. – powlo Jan 9 '15 at 11:43
7

As per the other answer and the documentation, the standard scope (the number of records processed in each iteration) size is 200. The maximum size you can specify for the second parameter is 2000. What this means is that the maximum number of records processed in one execute iteration is 2000.

You are creating 5000 Contacts for your test, so no matter what you set your scope parameter to be (the second parameter to Database.executeBatch) you will run into this problem because 3 iterations would run, e.g.

iteration 1 (2000 records)
iteration 2 (2000 records)
iteration 3 (1000 records)

To fix this, simply reduce the number of Contacts you create to be the same size (or less) than the scope size of the batch (i.e., if you just create 200 instead of 5000, you should have no problems).

| improve this answer | |
  • 1
    So essentially the maximum number of records that are batchable in a single test is 2000. Any larger than that and the batch will be split. This will in turn violate the "one batch per test" rule. – powlo Jan 8 '15 at 16:44
  • 1
    Yes, exactly right, it's a pain to workaround. – Phil Hawthorn Jan 8 '15 at 16:46
2

I`ve encountered the same thing in a unit test. What I did is to limit the batch size. If you take a look at the Database class it has the method executeBatch(Object, Integer).

That method has a second integer parameter which is called the scope. This determines the number of records to be passed into the execute method for batch processing. From what I've experimented the limit is 200.

So in your unit test you might have

    account = new Account(Name='Unassigned');
    integer batchSize = 200;
    insert account;

    bucket_o_contacts = new List<Contact>();
    for (Integer i = 0; i < batchSize; i++){
        c = new Contact(LastName='Contact' + i, Account=account);
        bucket_o_contacts.add(c);
    }

    Test.startTest();
    Database.executeBatch(new BulkInsert(bucket_o_contacts), batchSize);
    Test.stopTest();

Hope it helps.

| improve this answer | |

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.