2

Given a project based on fflib-apex-common and using fflib-ApexMocks for unit testing, I would like to mock Custom Metadata records and stop using real org values. Custom metadata queries are encapsulated into 'configuration' classes using static methods. Following an example:

public with sharing class AppointmentConfiguration {

    public static Integer getMinutesBlockedAfterAppoitment(){
        String value = AppointmentSettings__mdt
            .getInstance('MINUTES_BLOCKED_AFTER_APPOITMENT').value__c;
        return Integer.valueOf(value);
    }
        [...]
}

Therefore, in the service layer:

AppointmentConfiguration.getMinutesBlockedAfterAppoitment();

Is there any option to mock it?

Thanks

1 Answer 1

6

For me, it totally makes sense to get a record of AppointmentSettings__mdt from the selector's method so that it can be canonically mocked using fflib-ApexMocks in unit tests:

  1. Create an AppointmentSettingsSelector class:
public with sharing class AppointmentSettingsSelector extends fflib_SObjectSelector {

    public override SObjectType getSObjectType() {
        return AppointmentSettings__mdt.SObjectType;
    }

    public override List<SObjectField> getSObjectFieldList() {
        return new List<SObjectField>{
                AppointmentSettings__mdt.Value__c
        };
    }

    public AppointmentSettings__mdt byDeveloperName(String devName) {
        return AppointmentSettings__mdt.getInstance(devName);
    }
}
  1. Set Up the Application class to inject dependencies in run-time.
public with sharing class Application {

    public static final fflib_Application.SelectorFactory SELECTOR
        = new fflib_Application.SelectorFactory(
            new Map<SObjectType, Type>{
                AppointmentSettings__mdt.SObjectType => AppointmentSettingsSelector.class
            }
        );
}
  1. Delegate querying the AppointmentSettings__mdt record to the injected AppointmentSettingsSelector instance:
public with sharing class SomeClass {

   public static void someMethod() {
       AppointmentSettingsSelector selector = (AppointmentSettingsSelector) 
           Application.SELECTOR.newInstance(AppointmentSettings__mdt.SObjectType);
       String value = selector.byDeveloperName('MINUTES_BLOCKED_AFTER_APPOITMENT')
           .value__c;
       [...]
   }
        
}
  1. Set up a Unit Test method:
@IsTest
static void test() {
    // Mocks
    fflib_ApexMocks mocks = new fflib_ApexMocks();
    AppointmentSettingsSelector selectorMock = (AppointmentSettingsSelector)
        mocks.mock(AppointmentSettingsSelector.class);
    
    // Instantiating and editing a CMT record are allowed in memory within Apex,
    // but DML operations are not allowed, although it is enough for mocking.
    AppointmentSettings__mdt settingsMock = new AppointmentSettings__mdt();
    settingsMock.Value__c = 'mock value';
    // Given
    Application.SELECTOR.setMock(selectorMock);
    mocks.startStubbing();        
    mocks.when(selectorMock.sObjectType())
        .thenReturn(AppointmentSettings__mdt.SObjectType);
    mocks.when(selectorMock.byDeveloperName('MINUTES_BLOCKED_AFTER_APPOITMENT'))
        .thenReturn(settingsMock);
    mocks.stopStubbing();
}
1
  • 1
    I couldn't have said it better! Sometimes, you need to use the JSON serialize/deserialize approach to mock the MDT (fflib_ApexMocksUtil or its equivalent) as you can't use assignment statements for some of the fields
    – cropredy
    Commented Nov 1, 2021 at 23:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .