Climbing towards the Apex of Apex: The Delegate Pattern
Tags: salesforce
Published: 2020-06-02 14:07:31

# Climbing towards the Apex of Apex: The Delegate Pattern
Recently I've been avoiding writing test coverage for a Massive View Controller. This view controller is a single apex class that held enough logic to power an extremely complex, multi-section Flow component built on the Aura framework.

If you develop in Apex for the Salesforce platform, especially working with legacy code, you've encountered this. The best practice I've learned has been to use Facades and get more abstraction. This is great when writing new code. When you inherit someone else's bad decisions, things are a bit more complex.

My first step in starting to bring these pieces of code up to par is to start refactoring by abstraction. Interfaces, data accessors, factories, and mocks are some of the top tools in my toolbag, but one issue I've had is creating configurable mocks. It's easy in languages where you can build a closure or replace functionality on the fly during runtime. Apex is not like this, and it started to get to me one day.

Instead of giving up and inserting random test data into the test database, I took another approach and brought in an old standby from working with iOS: Delegates.

## Testing with the Delegate Pattern
I'll say it outright, there's a lot of work involved in building up this pattern, but once the basic setup is done it's easy to improve your delegates and get extremely fine control of your mocked dependencies. Niceties like flagging a method to throw exceptions can help you test some of those pesky catch blocks that exist to handle exceptions that aren't possible to trigger in test code.

Let's dive in. Here's a sample of the code under test after refactoring to use a data accessor:

```
public with sharing class MassiveViewController {
  @TestVisible
  private static SpecializedDAI dataAccessor = new SpecializedDAO();

  @AuraEnabled
  public static List<Asset> methodUnderTest(String sObjectId) {
    List<Asset> result = new List<Asset>();

    Id recordId = Id.valueOf(sObjectId);
    if (recordId.getSobjectType() == Contact.getSobjectType()) {
      try {
          result = dataAccessor.getAssetsForContact(contactId);
      } catch (Exception e) {
        // Handle the exception
        System.debug(e);
    }
  }

  return result;
}
```

The first step is to pick an appropriate place to bring the Delegate pattern in. The data accessor and mock seem well suited: they're are fairly class-specific, and the mock would need to provide various types of results to effectively implement the interface it was mocking. Previously I would have built up a set of results returned as a cast of some generic type, e.g. `(List<Asset>) List<SObject>`. This is messy, and the more I did it the less happy I was.

Instead I took the plunge! Have a look at this sample:

```
@IsTest
public with sharing class SpecializedDAMock implements SpecializedDAI {
    public interface GetAssetsForContactDelegateI {
        List<Asset> results();
    }

    public class GetAssetsForContactDelegate implements GetAssetsForContactDelegateI {
        public List<Asset> results() {
            return new List<Asset>();
        }
    }

    public GetAssetsForContactDelegateI getAssetsForContactDelegate = new GetAssetsForContactDelegate();
    public List<Asset> getAssetsForContact(Id contactId) {
        return getAssetsForContactDelegate.results();
    }
}
```

I could have implemented a little less, such as using an `abstract` class for the delegate, but I like to provide reference implementations when I'm building out test mocks as a nicety for myself and my team members who may be less familiar with the patterns I bring to the codebase. I also prefer composition, but the same result can be accomplished with virtuals and overrides if you prefer inheritance.

So how do you use this sort of pattern?

```
@IsTest
public with sharing class MassiveViewControllerTest {
    public class GetAssetsForContactDelegate implements SpecializeddDAMock.GetAssetsForContactDelegateI {
        public Boolean shouldThrow = false;
        public List<Asset> results = new List<Asset>();
        public Integer resultsCallCount = 0;

        public override List<Asset> results() {
            resultsCallCount++;

            if (shouldThrow) {
                throw new TestUtility.MockException('Delegate set to throw.');
            }

            return results;
        }
    }
    
    @IsTest
    private static void methodUnderTest_GivenAContactId_ShouldReturnAListOfAssets() {
        GetAssetsForContactDelegate delegate = new GetAssetsForContactDelegate();
        delegate.results = new List<Asset> { new Asset() };
        
        SpecializedDAMock mockDA = new SpecializedDAMock();
        mockDA.getAssetsForContactDelegate = delegate;
        MassiveViewController.dataAccessor = mockDA;

        String contactId = TestUtility.getFakeId(Contact.getSObjectType());

        Test.startTest();
        List<Asset> results = MassiveViewController.methodUnderTest(contactId);
        Test.stopTest();

        System.assert(results != null, 'The results should exist');
        System.assertEquals(1, results.size(), 'The results should have one element.');
    }

    @IsTest
    private static void methodUnderTest_ShouldCatchThrownExceptionsAndReturnAnEmptyListOfAssets() {
        GetAssetsForContactDelegate delegate = new GetAssetsForContactDelegate();
        delegate.results = new List<Asset> { new Asset() };
        delegate.shouldThrow = true;

        SpecializedDAMock mockDA = new SpecializedDAMock();
        mockDA.getAssetsForContactDelegate = delegate;
        MassiveViewController.dataAccessor = mockDA;
        
        String contactId = TestUtility.getFakeId(Contact.getSObjectType());

        Test.startTest();
        List<Asset> results = MassiveViewController.methodUnderTest(contactId);
        Test.stopTest();

        System.assert(results != null, 'The results should exist');
        System.assertEquals(0, results.size(), 'The results should have no elements.');
    }

    @IsTest
    private static void methodUnderTest_GivenALeadId_ShouldReturnAnEmptyListOfAssets() {
        GetAssetsForContactDelegate delegate = new GetAssetsForContactDelegate();
        
        SpecializedDAMock mockDA = new SpecializedDAMock();
        mockDA.getAssetsForContactDelegate = delegate;
        MassiveViewController.dataAccessor = mockDA;
        
        String leadId = TestUtility.getFakeId(Lead.getSObjectType());

        Test.startTest();
        List<Asset> results = MassiveViewController.methodUnderTest(leadId);
        Test.stopTest();

        System.assert(results != null, 'The results should exist');
        System.assertEquals(0, results.size(), 'The results should have one element.');
        System.assertEquals(0, delegate.resultsCallCount, 'The controller should not have called the DA');
    }
}
```

The power is right there. I'm not hitting the database, I'm not building out many different mockable interfaces for each individual DA method, and I'm no longer casting just for convenience. Even better, I don't have to think about the validation rules in place that would normally require setting up complex relationships for zero benefit. Additionally, I can add extra test functionality in the delegate to ensure the unit under test isn't providing false positives, and after building up a single delegate I was able to cover all three cases with ease.

## What's next?
Some might call this over-engineered, but I see this as a stepping stone to better Apex codebases regardless of the fact that it's stuck in the days of Java 5 capabilities. I'll be taking this into "real" codebases soon to provide closure-like functionality and improve the re-usability of various other abstractions. I even see it as a promising way to ease implementation of Promise-like asynchronous code on top of Queueables.