How to mock Salesforce External Objects

James Boutel
Slalom Technology
7 min readSep 8, 2021

--

Introduction

External objects are great for viewing data within Salesforce without needing to utilize storage limits. On a recent project, I had to access external objects via Apex classes to display information within Lightning Web Components when writing test classes I found this to be more challenging than those based on custom objects. This blog details some of the tips and tricks that I found which are not available in the documentation.

What are external objects?

External objects are similar to custom objects, but external object record data is stored outside your Salesforce organization; Salesforce Connect is the magic that drives this functionality. For example, perhaps you have data that’s stored on-premises in an enterprise resource planning (ERP) system. Instead of copying the data into your org, you can use external objects to access the data in real-time via web service callouts. The great thing about external objects is that they are updated in real-time.

Business Scenario

In the particular scenario for a client, an external system was used to manage and track Orders. The client wanted to be able to see these Orders within Salesforce so that Service reps could have a 360-degree view of the customer to enable them to communicate with their customers effectively and quickly using Salesforce’s Service Cloud.

Lightning Web Components were required to display these Order records because standard related lists did not meet the requirements. The client wanted to be able to search through the list as well as pick a specific Order from the list to then link to the parent object. In order to display these Order records in a Lightning Web Component, we must use an Apex Class to query the external Order records.

Building Test Classes for Apex Based on External Objects

When using external objects, you don’t need to write test classes for them unless you are referencing them in apex classes.

You might think that external objects would work like normal custom objects in a test class. In that, you can build your records in the test class and then insert them. Unfortunately, it doesn’t work like that, since the records that are presented to you in Salesforce aren’t normal Salesforce records, they are actually records created from an external system. You cannot simply create them in a test class and insert them, as you cannot perform standard DML statements on external object records. If you try to do this, you will receive an error and the test class will not be deployable. Instead, you must “mock” the external object records and make your test class think that you are sending it records that already exist.

How can you mock an external object?

You can mock an external object by using a virtual class and making some amendments to your main code. There are 5 parts to this:

· The Object Manager

· The virtual class (the mock interface)

· The mock interface override class

· Calling the overridden mock interface and setting the mock using the Object Manager from within your test method

· Calling the method in the Object Manager anytime you are querying your external object in other apex classes

For this example, we will be using an external object called Orders__x.

The Object Manager:

public class ObjectManager{//We create a static instance of the class that can be overriddenstatic MockInterface instance = new MockInterface();/*@description Method that returns the records that are passed to it**********************************************************************************************/public static List<SObject> records(List<SObject> records){return instance.passThrough(records);}/*@description Method that is usable in a test class. Used to set the mock (SObject type) that this class will return.**********************************************************************************************/@TestVisible static void setMock(MockInterface mock){instance = mock;}}

This class is the utility class that all of the functional apex classes call to get information. The first thing we instantiate is a static instance of the virtual class. We use this to change its data type when we reference it from the test class.

records()This method calls the passThrough() method and passes through the list of sObjects that it is given as a parameter.

setMock() — This is a @TestVisible method that we call from the test class in order to actually set the records we want to mock.

The virtual class (the mock interface):

public virtual inherited sharing class MockInterface{/*@description A method that is overriden in the test class. When used outside of a test, returns the records. When used in a test class, will return the overridden method in the class that is defined in the test class, this overidden method returns a list of whatever object is defined in the test class **********************************************************************************************/public virtual List<SObject> passThrough(List<SObject> records){return records;}}

Here is an example of the virtual class. This is the interface class that we use for the dependency injection, it has a default method for returning records. Notice the virtual keyword in the class declaration. A virtual class is just a class that can be extended and has methods that can be overridden.

passThrough()At its core, this method simply returns the list of sObjects it is passed as a parameter. You will notice, however, that this is a virtual method, which means it can be overridden (more on that later).

The mock interface override class:

public class MockInterfaceOverride extends MockInterface{final List<SObject> objectList;MockInterfaceOverride(List<SObject> mockRecords){this.objectList = mockRecords;}public override List<SObject> passThrough(List<SObject> records){return objectList;}}

This is a class that extends our virtual MockInterface class we made in the previous step. We use this class to set our mock records within each test method. The constructor of this local class is used to set the local variable objectList. This will be our list of records that we will pass.

The next method in the local class is the passThrough() method which has been overridden from the virtual MockInterface class. We override this so we can return the local variable objectList that we have instantiated when the passThrough() method is called. At this point you may be wondering “when is the passThrough() method called?”.. that is coming up next!

The main test class:

@isTestpublic with sharing class TestClass {@isTeststatic void testOrdersApexClass(){List<Orders__x> orders = new List<Orders__x>();Orders__x order = new Orders__x(OrderNumber__c = ‘1234567890’);orders.add(orders);ObjectManager.setMock(new MockOrdersOverride(orders));Test.startTest();getOrders();Test.stopTest();//do assertions}

Here is an example of the test class we are using to test our apex class. We call ObjectManager.setMock() in order to pass the records we want to mock to our MockInterfaceOverride class.

How to implement in your main apex class where you are querying your external object:

public static List<Orders__x> getOrders(){//Query OrdersList<Orders__x> orders=ObjectManager.records([SELECT Id, OrderNumber__c FROM Orders__x]);return orders;}

This is a method within our apex class that is used to get Orders. As you can see, we call the records() method from the ObjectManager class to query the external object records.

How does this work?

Let’s take a closer look at it by putting it all together…

When we are not running a test

When we call the records() method it is expecting a list of sObjects, as defined in our ObjectManager class. So, by passing a query of the external object records, we are satisfying that parameter requirement. The code then calls the passThrough() method from the MockInterface class, which (if we are not running a test method, and therefore have not overridden this method) will simply return the records we have passed to it, essentially meaning that nothing has really happened. We will receive back the records we expect from the external system (see figure 1).

Figure 1: Not running a test

When we are running a test

When we are running a test it works slightly differently, as we have overridden the passThrough() method and called setMock() in our test method. This time, when the getOrders() method is called from our test method, it calls the records() method just as above. However, now when the passThrough() method is called, it uses the overridden method in the MockInterfaceOverride class instead (see figure 2).

Figure 2: Running a test

Why does it use the overridden method instead? Because we have called setMock() in the test method. This tells the code to use this overridden class rather than the original class.

So when instance.passThrough() is called, instance is now our local class where we have overridden the passThrough() method.

The overridden version of the passThrough() method actually returns the local variable objectList instead of the records that were passed to it, and what is in the objectList variable? Well, the list of records that we made in our test method. We set this variable when we called the setMock() method in our test method, it is simply a list of external object records that we have not inserted.

There you have it. That’s how you can mock external object records in a test class. You can download the code from this GitHub link: https://github.com/JamesBoutel1a4/Salesforce-External-Object-Mock-Example

Please see below for some Salesforce help links on this subject:

https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_external_objects.htm

https://help.salesforce.com/articleView?id=sf.external_object_define.htm&type=5

--

--