Understanding how ApexMocks works

The whenCarStartCalledDashboardAndEngineInitialised unit test method in the CarTestApexMocksDemo test class uses the ApexMocks library. The structure of this unit test is similar to those we have looked at so far in this chapter. It creates the mocks, runs the method to test, and asserts the results.

The big difference is that you do not need to write any code for classes you wish to mock. Instead, you use the ApexMocks framework to configure your mock responses and to assert what methods were recorded. Then, through its integration with the Apex Stub API, it creates a stub that wraps itself around any class you wish to mock.

The main class within the ApexMocks framework is fflib_ApexMocks. This class implements the System.StubProvider interface for you. It automatically echoes any mock responses to mocked methods called and records that they got called and with what parameters. This applies to all public methods on the classes you ask it to mock for you.

Much of the skill in using ApexMocks is knowing how to configure the mocked responses it gives when mocked methods are called, and how to write matching logic to assert not only when a method was called, but how many times and with what parameters. This latter facility is incredibly powerful and opens up some sophisticated options for verifying the behavior of your code.

The following code initializes the ApexMocks framework (every test method needs this):

@IsTest 
private static void
whenCarStartCalledDashboardAndEngineInitialised() { fflib_ApexMocks mocks = new fflib_ApexMocks();

Next, create the mock implementations that you need:

   // Given 
   Dashboard mockDashboard = 
(Dashboard) mocks.factory(Dashboard.class); Engine mockEngine =
(Engine) mocks.factory(Engine.class);

The following code replaces the need for you to write mock methods that return test values during the execution of the code being tested. In ApexMock terms, this configuration code is known as stubbing code. You must surround stubbing code with startStubbing and stopStubbing method calls, as shown here:

   mocks.startStubbing(); 
   mocks.when(mockEngine.isRunning()).thenReturn(true); 
   mocks.stopStubbing();
The syntax gets a little tricky to understand at this stage, but you will get used to it, I promise! As I stated earlier, much of the inspiration for the ApexMocks API was taken from the Java Mockito library. So much so that articles and documentation around the internet should also aid you in learning the API, as well as the documentation provided in the GitHub repo.

Your eyes are not deceiving you; the preceding code is calling the method you're attempting to mock a response for. First of all, this is a compile-time reference, so it is much safer than a late-bound string reference, as we used when using the Apex Stub API raw.

Secondly, because mockEngine points to a dynamically generated stub, it is not calling the real implementation of this method. Instead, it is registering in the framework that the next method call, thenReturn, should store the given value as a response to this method. This will be given back at a later point in the test when that mocked method is called again, this time in the test case context. In this case, when the Car.isRunning method calls engine.isRunning (refer to the Car code if you need to refresh your memory as to what the implementation does), it returns the value true.

The next part of the unit test, the actual code being tested, is more familiar:

   // When         
   Car car = new Car(mockEngine, mockDashboard); 
   car.start(); 

Ensuring that the dashboard.initialise and engine.start methods have been called, as well as that the value returned by Car.isRunning is as expected, is also a little different from when calling the handwritten mock class methods:

   // Then 
   System.assertEquals(true, car.isRunning()); 
   ((Dashboard) mocks.verify(mockDashboard, 1)).initialise(); 
   ((Engine) mocks.verify(mockEngine, 1)).start();        
}
Due to the explicit casting requirements of Apex, the last two lines do not quite match what you would see in an equivalent Java Mockito example. This can make the syntax harder to read. In Java Mockito, it would look as follows:

mocks.verify(mockDashboard, 1).initialise();
mocks.verify(mockEngine, 1).start();

The assertion code is a mixture of traditional System.assert and calls to the fflib_ApexMocks.verify method, followed by a call to the methods being mocked. Once again, this compiler-checked reference is preferable to late-bound string references.

The verify method takes an instance of the mocked object and number. This number allows you to assert that the method was called once, and only once. Once the verify method exits and the mocked method is called, the expected result passed to the verify method is checked. If it is not met, either because the method was not called, or it was called more than once, the framework actually has the mocked method use System.assert to halt the test with an exception message describing why.

Another example is the whenUpdateRPMsCalledMessageIsDisplayed test method in the DashboardTestApexMocksDemo class. This utilizes a more advanced form of asserting that the desired mocked method was called. This approach not only validates that it was called, but with specific parameter values:

@IsTest 
private static void whenUpdateRPMsCalledMessageIsDisplayed() { 
         
    fflib_ApexMocks mocks = new fflib_ApexMocks(); 
         
    // Given 
    Display mockDisplay = (Display) mocks.factory(Display.class); 
         
    // When 
    Dashboard dashboard = new Dashboard(mockDisplay); 
    dashboard.updateRPMs(5000); 
         
    // Then 
    ((Display) mocks.verify(mockDisplay, 1)).
showMessage(10, 10, 'RPM:5000'); }

The preceding use of the verify method is no different from the previous test. However, the following mocked method call illustrates a very cool feature of the ApexMocks verification logic. The parameters passed in the call to the mocked method are used by the framework to compare with those recorded during the prior execution of the method during the preceding test. If any differences are detected, an assertion is thrown and the test is stopped.

Some of the preceding examples include a System.assert method call inline. Other unit tests you write may not require the use of this statement as they solely assert that the correct dependent methods have been called through the verify method. This can lead to a false positive in the Salesforce Security Review scanner since the verify method is not recognized as a method verifying quality. If this happens, you can add a comment neutral assert to pacify the scanner, or record the reason why in the false-positive document you attach to the review.
..................Content has been hidden....................

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