As we saw earlier, the supporting library or Apex Enterprise patterns provides methods that provide a Dependency Injection facility through the factories in the Application
class. This facility is also compatible with the use of ApexMocks and Apex Stub API. The following sections contain examples of the use of ApexMocks to unit test the layers within the application architecture introduced in earlier chapters.
The following test can be found in the RaceControllerTest
class and demonstrates how to mock a service layer class:
@IsTest private static void whenAwardPointsCalledIdPassedToService() { fflib_ApexMocks mocks = new fflib_ApexMocks(); // Given RaceServiceImpl mockService = (RaceServiceImpl) mocks.factory(RaceServiceImpl.class); Application.Service.setMock(RaceService.class, mockService); // When Id raceId = fflib_IDGenerator.generate(Race__c.SObjectType); RaceController raceController = new RaceController( new ApexPages.StandardController( new Race__c(Id = raceId))); raceController.awardPoints(); // Then ((RaceServiceImpl) mocks.verify(mockService, 1)).awardChampionshipPoints(new Set<Id> { raceId }); }
Consider the following notable aspects from the above test code:
The following test method can be found in the RaceServiceTest
class and demonstrates how to mock classes implementing the unit of work, domain, and selector layers:
@isTest private static voidwhenAwardChampionshipPointsCallsDomainAndCommits() { fflib_ApexMocks mocks = new fflib_ApexMocks(); // Given - Create mocks fflib_SObjectUnitOfWork mockUow = (fflib_SObjectUnitOfWork)mocks.factory(fflib_SObjectUnitOfWork.class); RacesSelector mockSelector = (RacesSelector) mocks.factory(RacesSelector.class); Contestants mockDomain = (Contestants) mocks.factory(Contestants.class); // Given - Configure mock responses Id testRaceId = fflib_IDGenerator.generate(Race__c.SObjectType); Id testContestantId =fflib_IDGenerator.generate(Contestant__c.SObjectType); List<Race__c> testRacesAndContestants = (List<Race__c>) fflib_ApexMocksUtils.makeRelationship( List<Race__c>.class, new List<Race__c> { new Race__c ( Id = testRaceId) }, Contestant__c.Race__c, new List<List<Contestant__c>> { new List<Contestant__c> { new Contestant__c (Id = testContestantId) } }); mocks.startStubbing(); mocks.when(mockSelector.SObjectType()). thenReturn(Race__c.SObjectType); mocks.when(mockSelector.selectByIdWithContestants(new Set<Id> { testRaceId })).thenReturn(testRacesAndContestants); mocks.when(mockDomain.SObjectType()).thenReturn(Contestant__c.SObjectType); mocks.stopStubbing(); // Given - Inject mocks Application.UnitOfWork.setMock(mockUow); Application.Selector.setMock(mockSelector); Application.Domain.setMock(mockDomain); // When RaceService.awardChampionshipPoints(new Set<Id> { testRaceId }); // Then ((RacesSelector) mocks.verify(mockSelector, 1)).selectByIdWithContestants(new Set<Id> { testRaceId }); ((Contestants) mocks.verify(mockDomain, 1)).awardChampionshipPoints(mockUow); ((fflib_SObjectUnitOfWork) mocks.verify(mockUow, 1)).commitWork(); }
Consider the following notable aspects from the preceding test code:
fflib_ApexMocksUtils.makeRelationship
method is used to construct in memory a list of records containing child records. This utility method (part of ApexMocks) works around a platform limitation in mocking this data construct. This method is very useful for returning more complex record sets returned from selector methods your tests are mocking.Application.Selector.setMock
and Application.Domain.setMock
methods to inject the mock implementations of the RacesSelector
and Contestants
domain classes, the mocking framework is used to mock responses to the SObjectType
methods for the mocked instances of these classes, which is called by the setMock
methods. Mocking this method ensures that the mocked selector instance gets correctly registered with the Selector factory. fflib_IDGenerator.generate
method (part of ApexMocks) is used to generate an in memory ID that is used to populate the mock records returned from the mock selector. These same ID's are used to confirm what was passed into the Selector as the same ID's were passed to the Service method.Contestants
domain class gained a default constructor as required by the Test.createStub
method, used by the fflib_ApexMocks.factory
method. The following test method can be found in the ContestantsTest
class and demonstrates mocking selector method responses to return database rows created in memory. it also shows that asserting the same records are subsequently passed to a mocked unit of work for update:
@IsTest private static void whenAwardChampionshipPointsUowRegisterDirty() { fflib_ApexMocks mocks = new fflib_ApexMocks(); // Given fflib_SObjectUnitOfWork mockUow = (fflib_SObjectUnitOfWork)mocks.factory(fflib_SObjectUnitOfWork.class); Application.UnitOfWork.setMock(mockUow); Id testContestantId =fflib_IDGenerator.generate(Contestant__c.SObjectType); List<Contestant__c> testContestants = new List<Contestant__c> { new Contestant__c ( Id = testContestantId, RacePosition__c = 1 )}; // When Contestants contestants = new Contestants(testContestants); contestants.awardChampionshipPoints(mockUow); // Then ((fflib_SObjectUnitOfWork) mocks.verify(mockUow, 1)).registerDirty( fflib_Match.sObjectWith( new Map<SObjectField, Object>{ Contestant__c.Id => testContestantId, Contestant__c.RacePosition__c => 1, Contestant__c.ChampionshipPoints__c => 25} )); }
Consider the following notable aspects from the preceding test code:
awardChampionshipPoins
method being unit tested is then called to determine if it correctly interacts with the unit of work.SObject
passed to the mocked fflib_SObjectUnitOfWork.registerDirty
method is the expected method. The matcher is also helping the test code confirm that the correct championship points have been calculated. For more information about ApexMock maters refer to the earlier section.The main logic to be tested by selector methods is that of constructing SOQL strings dynamically. Ideally it would be good if it were possible to mock the Database.query
method and thus unit tests could be written around complex selector methods to assert the SOQL strings generated. As this is currently not possible, there is little benefit in mocking selector classes. Your selector classes can instead obtain coverage and validation through their executions as part of your integration tests.
3.146.37.250