Only way to work with External Objects (Salesforce Connect) in Apex - Part 1 (Mocking Records and Replacing SOQL)
Background
External Objects or the Salesforce Connect framework to be more precise are a way expose (view, search, modify ... essentially everything you can do with SObjects but not quite) data that's stored outside a Salesforce org. Please refer to the following link for more information on what sort of data can be connected through Salesforce connect.Personally the cost associated with a single external object connection and the uncertainty of maintaining a connection with the external data source make it a hard choice right of the bat. Alternatively having a middle ware to sync data periodically and having webservices exposed by the external system (or the middleware) to update the data from SFDC meets most client needs. However, in some cases Salesforce connect is the only choice, and in those cases it is important to know how to work with it in Apex in a maintainable way.
Problem
If you have never worked (written unit tests) with External Objects you will go under the assumption that Salesforce must have built some kind of abstraction to replicate dml behaviour when unit testing with External objects. Well you will be wrong and you might see an error similar to the one shown below. Once you come to that realization you will do some googling for a mocking framework for external objects. You will be disappointed but apparently there is something coming. But if we use the right design pattern we can facilitate mocking data pretty easily.
Encapsulation is key
From the research (very helpful article by Graham Barnard) I have done. The only way to work properly with external objects is to encapsulate it into a class (Model). The figure below illustrates a pattern that can be used. This is the only logical way of working with External Objects to facilitate unit test data creation with the additional benefit of localizing your queries to a single class (Model).
Example
The base class provides an elegant way of encapsulating any test related functionality and easily makes it accessible to all child models.
1 2 3 4 5 6 7 8 9 10 11 12 | public with sharing virtual class ExternalObjectModelBase { @TestVisible protected List<SObject> mockedRecords = new List<SObject>(); public void addTestRecord(SObject record) { mockedRecords.add(record); } public void addTestRecords(List<SObject> records) { mockedRecords.addAll(records); } } |
Sample implementation of the the base class using the Orders__x external object from the Salesforce Connect quickstart trailhead package. Making it Singleton is essential for inheriting from the base class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public with sharing class ExternalOrderModel extends ExternalObjectModelBase{ private static ExternalOrderModel uniqueInstance; //Private Constructor for Singleton private ExternalOrderModel() { } //Singleton getter public static ExternalOrderModel getInstance() { if(uniqueInstance == null) uniqueInstance = new ExternalOrderModel(); return uniqueInstance; } public Orders__x findByOrderId(Integer orderId) { List<Orders__x> orderList = [SELECT customerID__c, orderDate__c, orderID__c, shippedDate__c From Orders__x Where orderId__c =: orderId]; return (Test.isRunningTest()) ? (Orders__x) mockedRecords[0] : (orderList.size() > 0) ? orderList[0] : null; } public List<Orders__x> findOrdersByCustomerId(Integer customerId) { List<Orders__x> orderList = [SELECT customerID__c, orderDate__c, orderID__c, shippedDate__c From Orders__x Where customerID__c =: customerId]; return (Test.isRunningTest()) ? (List<Orders__x>) mockedRecords : (orderList.size() > 0) ? orderList : null; } } |
Sample test class that covers the ExternalOrderModel. One thing to point out is, for the createTestData method we cannot use the @testSetup annotation since it runs in different context than the tests :(.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | @isTest private class ExternalOrderModelTest { @isTest static void testFindByOrderId() { createTestData(); Test.startTest(); Orders__x order = ExternalOrderModel.getInstance().findByOrderId(1); System.assertEquals(order.ExternalId, '123'); System.assertEquals(order.OrderID__c, 1); System.assertEquals(order.CustomerID__c, 123); Test.stopTest(); } @isTest static void testFindOrdersByCustomerId() { createTestData(); Test.startTest(); List<Orders__x> orderList = ExternalOrderModel.getInstance().findOrdersByCustomerId(123); System.assertEquals(orderList.size(), 2); Test.stopTest(); } private static void createTestData() { //Create Order Data Orders__x order1 = new Orders__x( ExternalId = '123', OrderID__c = 1, CustomerID__c = 123, orderDate__c = Date.today().addDays(-5), shippedDate__c = Date.today().addDays(-3) ); Orders__x order2 = new Orders__x( ExternalId = '124', OrderID__c = 2, CustomerID__c = 123, orderDate__c = Date.today().addDays(-5), shippedDate__c = Date.today().addDays(-3) ); List<Orders__x> orderList = new List<Orders__x>{order1, order2}; ExternalOrderModel extOrderModel = ExternalOrderModel.getInstance(); extOrderModel.addTestRecords(orderList); } } |
Conclusion
Although this is not the only way of working with External object in Apex. I find it to be the best since it keeps the code localized and makes unit testing a breeze. Please let me know if you have any feedback or improvements. I hope this will be helpful for salesforce devs that are facing the same challenges as us with External Objects.
Salesforce connect tightly integrate external data sources into your apps, giving employees a unified customer view, on any device. Admins can use point-and-click tools to bring data in or connect data between Salesforce instances, and developers can code simple APEX adapters to connect data from any web service API. Companies who are unaware of how to use it can take Salesforce Training to make them fully knowledgeable.
ReplyDeleteThanks for sharing useful information
ReplyDeletesoftware testing training institute in chennai
Thanks for sharing useful information
ReplyDeletesoftware testing training institute in chennai
Such a wonderful article and I feel that it is best to write more on this topic. Thank you so much because i learn a lot of ideas about it. Keep posting...
ReplyDeleteDigital Marketing Course In Kolkata
Such a wonderful article and I feel that it is best to write more on this topic. Thank you so much because i learn a lot of ideas about it. Keep posting...
ReplyDeleteDigital Marketing Course In Kolkata
This is because these customers will not only be loyal in case of losses but will also stick to the business when new competitors enter the market. Salesforce training in Chennai
ReplyDeletePower BI training in Chennai provides comprehensive coverage of the Microsoft Power BI tool. This certification course helps you gain clarity on concepts such as Power BI architecture, Service, Desktop, and Mobile Apps, reports, visualisation, etc. Get the best Power BI course from top experts. Enroll now in the best Power BI Training in Chennai and get certified at FITA Academy with 100% placement assistance.
ReplyDeleteTags: Power BI Online Course
Power BI Course in Bangalore
This comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteThank you for this beautiful information
ReplyDeleteProgramming Languages Classes in Bangalore
Python Training In Bangalore
Best Training Institute In Marathahalli
Python Training In Marathahalli