One Batch Apex to rule them all (Part II)

Program to interfaces, not implementations

Advertisements

Watch Out!! Bulkified zone!!

In the firsts part of this post, we saw how to isolate the batch apex logic from the operations performed. You can find the full code in the versiononefirst repository.

But today, we are going to extend it further by making our batch apex handler able to perform a list of operations. We’ll also be adding more features and introducing some new concepts as we progress. Get ready… Let’s go!

The plan

To address the challenge, we’ll change our strategy. We’ll create the BatchableHandler‘s unit tests first, so that we can anticipate and better understand our process needs. Our milestones are:

  1. Create this exciting and shining indexed list! Done!
  2. Make a unit tests that runs a list of command instances.
  3. Make the BatchableHandler able to take a list of commands.
  4. Make the BatchableHandler class able to run, one by one, the given operations until there is nothing else to do.

We have a plan! Let’s get our feet wet.

Getting a list of commands

Changing the BatchableHandler to take a list of commands, looks pretty straight forward, but which command instance would you use in the BatchableHandlerTests class to test the process effectively? There are many benefits of writing code based on interfaces. But today we specially will benefit from “receiving” an interface in the constructor and relying on it to perform whatever operation it does. This is known as dependency injection. Thanks to this, we’ll be able to create our own test implementation of a command. This will make the unit test independent from any specific process in our code base. In other words, it will be decoupled!

In our batch apex case, as this is an asynchronous process, we need to make persistent changes we can query and check afterwards to ensure the code is working correctly. I decided to use the Account object, so that I can make some amendments like adding extra text to the existing records’ name. My Batchable test class by now looks like this:

@isTest(SeeAllData=false)
public class BatchableHandlerTests
{
    private class NameAssembler implements Batchable
    {
        public String AccountNameSuffix {get; set;}

        public NameAssembler()
        {
            AccountNameSuffix = '';
        }

        public String getQuery()
        {
            return 'SELECT Id, Name FROM Account LIMIT 1';
        }

        public void execute(List accounts)
        {
            for (Account acc : accounts)
                acc.Name += ', ' + AccountNameSuffix;

            update accounts;
        }
    }
}

We’ll use AccountNameSuffix property highlighted above to set the aforementioned extra text on the existing accounts’ Name field. So the unit test flow will be: create accounts, ask the BatchableHandler to run the NameAssembler command several times, and finally check that the Name field has changed on all the accounts. E.g.:

@isTest(SeeAllData=false)
public class BatchableHandlerTests
{
    private static List testAccounts;

    @TestSetup static void createAccount()
    {
        Account testAccount = new Account();
        testAccount.Name = 'Scissorhands, Eduard';
        insert testAccount;
    }

    // Check that BatchHandler successfully runs a couple of commands
    @isTest static void batchableHandler_runsAListOfCommands()
    {
        // Given
        NameAssembler commandA = new NameAssembler();
        commandA.AccountNameSuffix = 'Jack';
        NameAssembler commandB = new NameAssembler();
        commandB.AccountNameSuffix = 'Sparrow';
        List commands = new List{commandA, commandB};

        // When
        Test.startTest();
        BatchableHandler.runCommands(commands);
        Test.stopTest();

        // Then
        assertAccountNamesContain(commandA.AccountNameSuffix);
        assertAccountNamesContain(commandB.AccountNameSuffix);
    }

Done! You can imagine what the assertAccountNamesContain does, but you can visit the repository later anyway 😉 This is clearly exposing how the BatchableHandler should look and behave. Point 2 done!

To adapt the batch class to this requirement, seems that we have just to amend the constructor to take a list…

public class BatchableHandler implements Database.Batchable
{
    private List commands;

    public static Id runCommands(List commands)
    {
        if (commands == null || commands.isEmpty())
            return null; // Or say something relevant

        BatchableHandler batchableHandler = new BatchableHandler(commands);
        return Database.executeBatch(batchableHandler);
    }

    public BatchableHandler(List commands)
    {
        this.commands = commands;
    }
...

And then make the start and execute methods to work with such list. Easy! But wait, we are creating a batch apex class because our operations are very volume demanding. So the BatchHandler class should still be able to run such list with the same performance than it would have when making individual calls per command. Let’s see how.

Chaining commands

This is where the magic happens. Salesforce allows invoking a batch apex class in the finish method. Thanks to this, we can manage one command at a time, like previously, to finally chain a new call to run the next list of operations. This is as simple as the following:

public Database.QueryLocator start(Database.BatchableContext BC)
{
    return Database.getQueryLocator(commands[0].getQuery());
}

public void execute(Database.BatchableContext BC, List scope)
{
    // No error handling by now
    commands[0].execute(scope);
}

public void finish(Database.BatchableContext BC)
{
    if (commands.size() > 1)
    {
        commands.remove(0);
        runCommands(commands);
    }
    // else, notify the user that everything is done!
}

Whoa! We have just killed two birds with one stone. We will just process the first command in the list, but in the finish method, we’ll check if there is anything else to do. If so, we have just to pass the list again to the BatchableHandler but removing the first element before.

I can’t wait to see this in action. Let’s run our unit test!

batchable test success

If you still don’t, you’ll love that green tick. Nobody is perfect. I got some failures before getting a successful result, but that’s the best of TDD!! Your work is supported and driven by your unit tests.

Handling background processes

Ok. I still can see some things the BatchableHandler class can’t do. It is not handling errors nor notifying the user when it finishes, so I don’t know if all the chunks have been run.

This is fine for the version one, you know. We’ll extend this further. By the meantime, you can monitor your batch apex jobs execution at the setup page on your org. If you don’t know where, just type apex jobs inside the quick find box and select Apex Jobs.

Jobs executed by unit tests are not shown there, but you can do a test invoking our batch handler in the developer console. Try this for example:

ContactsService.CreateContactsBatchable commandA =
    new ContactsService.CreateContactsBatchable();
ContactsService.CreateContactsBatchable commandB =
    new ContactsService.CreateContactsBatchable();
List commands =
    new List{commandA, commandB};

BatchableHandler.runCommands(commands);

Go back to the apex jobs setup page. There you’ll see something like the following:

obtrta-batchjobssetup

It’s alive!! In later posts, we’ll do some improvements to this and filling some gaps the current implementation has. Stay tuned. See you soon!

Conclusions

Command pattern is even more powerful than we saw in the first part. It is designed to deal with any list of operations without knowing what the command does at all. It can even be used to undo operations! But the best of it (at least from my point of view) is how it decouples the operations from the process orchestrator. This is pretty useful, as you have seen, for unit testing. But also to assemble processes that work fine alone without adding dependencies between them. You should always strive for programming interfaces.

Why’s

  • Why don’t we make the batch class to implement the stateful interface? That’s a good question, indeed! And there are some reasons why I would keep this non stateful.
    • The batch class is intended to be a generic place to handle volume demanding operations. If we make it stateful, the data stored in memory, to be consumed on next chuncks, would necessarily have to be outside of this class (on the command class). That would make your command method to control states. This is against the idea of making it generic. Your command method should also be generic enough. It should work the same way regardless it is called once or twice (and synchronously or asynchronously). If it needs to remember other executions, maybe it is performing more than one operation. I would suggest to create additional commands.
    • The amount of information on the heap memory, could be out of your control. When working with batch apex, your are supposed to be managing any amount of data. Depending on which kind of data you keep in the heap and for how long, it could make your process to reach the heap size limit. Think that the more possible errors you have to handle, the less maintainable and readable is your code.
  • Why do we load data in inside a @TestSetup method? Couldn’t we create it inside the test method? Yes, we could do. It depends on your needs. If all the methods in the class are based in the same set of data, the method annotated with the @TestSetup keyword, saves you execution time. Data is created once, and reseted to its initial state on each method execution. The drawback of using @TestSetup to load your data, is that such information is created in a separate thread. This means that you cannot save the data created in a static variable in your class to use it later. If the data you need to prepare doesn’t need to be in the database, create it within your test.
  • Why do we use Test.startTest() and Test.stopTest() to wrap the batch execution? These methods have two uses indeed. One of them is about its meaning. We say that our test starts and finishes at those points. All the code surrounding is just test preparation or result checking. That makes the test method even more meaningful. But there is a second reason to be there. Wrapping the asynchronous process with them, ensures that it has finished before we make our assertions. You’ll need them always when you test batch classes. On the other hand, Test.startTest() resets the Salesforce limits count, so it also helps to check that our tested code doesn’t break by the additional code in the unit test.

Additional resources

A powerful unit testing tool you can use, once you gain more experience with dependency injection, is Paul Hardaker’s apex mocks framework. I encourage you to spend time on it. Although it requires a bit more advanced skills, you must give it a go. You will have no regrets.

Don’t miss the opportunity to check the Jesse Altman’s introduction too, who will be doing a talk about this at Dreamforce! “Speed Up and Simplify Unit Tests with ApexMocks”.

Acknowledgments

Thanks everybody for the warm welcome to the developer bloggers community. Special thanks  to all of you that contributed with your feedback and good advices. Of course, thanks to all of you who made this become socially alive. And thanks to you 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s