ApexMocks and Apex Enterprise Patterns

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.

Unit Testing a Controller Method

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:

  • Controllers in general use only service or selector classes. It could be considered a code review discussion point if other classes are used. Perhaps the separation of concerns within the controller needs further review?
  • This test simply creates a mock instance of the Service class and confirms the method was called with the correct parameter value from the controller. There is no mock response required. However, the ApexMocks framework does permit for exceptions to mocked. Thus another test could be written to confirm the error handling in the controller is working.

Unit Testing a Service Method

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:

  • The 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.
  • Prior to calling the 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.
  • The 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.
  • The Contestants domain class gained a default constructor as required by the Test.createStub method, used by the fflib_ApexMocks.factory method.

Unit Testing a Domain 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:

  • An in memory list of records is created in the test code and passed to the domain classes constructor. The awardChampionshipPoins method being unit tested is then called to determine if it correctly interacts with the unit of work.
  • A custom ApexMocks matcher is used to check that the 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.
  • As this test is using a mocked unit of work, no DML is performed.

Unit Testing a Selector Method

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.146.37.250