Robert - in response to your point about Custom Metadata being hard to set up in unit tests
I use the selector pattern to query all sobjects, including Custom Metadata, then, use mocking via dependency injection to return the results to the testmethod. This decouples the testmethod from any live custom metadata (and thus achieves test isolation). Here is a small fragment
Standard fflib Selector and apexmocks pattern follows; assumes an Application class as per fflib pattern.
Since you can't construct Custom Metadata label/developername, I resort to the JSON deserialize approach. Here, I'm using the SObject Fabricator repo but there are other ways to do this including maps, json strings, or other libraries. Once you go the dependency injection route to unit tests, it is inevitable you will need at some point to have to construct sobjects that aren't supported by new TheSObject(..)
. I liked SObject Fabricator for its expressiveness.
fflib_ApexMocks mocks = new fflib_ApexMocks();
// Given mockSelector results
MyCustomMetadata__mdt[] mockMdts = new List<MyCustomMetadata__mdt> {
(MyCustomMetadata__mdt) new sfab_FabricatedSObject (MyCustomMetadata__mdt.class) // [
.setField(MyCustomMetadata__mdt.Label,'foo')
.setField(MyCustomMetadata__mdt.SomeField__c,true)
.toSObject(),
(MyCustomMetadata__mdt) new sfab_FabricatedSObject (MyCustomMetadata__mdt.class) // [1] not troubleshooter
.setField(MyCustomMetadata__mdt.Label,'bar')
.setField(MyCustomMetadata__mdt.SomeField__c,false)
.toSObject()
};
// Given mock selector(s)
MyCustomMetadatasSelector mockMdtsSelector = (MyCustomMetadatasSelector) mocks.mock(MyCustomMetadatasSelector.class);
mocks.startStubbing();
mocks.when(mockMdtsSelector.sObjectType()).thenReturn(MyCustomMetadata__mdt.SObjectType);
mocks.when(mockMdtsSelector.selectByLabel(new Set<String>{'foo'}))
.thenReturn(new List< MyCustomMetadata__mdt> {mockMdts[0]});
mocks.when(mockMdtsSelector.selectByLabel(new Set<String>{'bar'}))
.thenReturn(new List<MyCustomMetadata__mdt> {mockMdts[1]});
mocks.stopStubbing();
// Given mocks injected
Application.Selector.setMock(mockCrsdToDescriptionsSelector);
// when code-under-test invoked
Test.startTest();
new MyClass().doStuff();
Test.stopTest();
// Then verify
.. asserts and/or mocks.verify
Note that if the selector method is called with a set containing only foo
, the first mocked custom metadata is retrieved by the dependency-injected selector. And, if called with a set containing bar
, the second mocked custom metadata is retrieved.
Since you can't construct Custom Metadata label/developername, I resort to the JSON deserialize approach. Here, I'm using the SObject Fabricator repo but there are other ways to do this including maps, json strings, or other libraries. Once you go the dependency injection route to unit tests, it is inevitable you will need at some point to have to construct sobjects that aren't supported by new TheSObject(..)
. I liked SObject Fabricator for its expressiveness.
I know you use your own testobject fabricator so adapt as necessary.