Implementing unit tests with CDI and mocking

The mock implementations of the interfaces can now also be implemented. These mock classes can be as simple or as complex as you desire, depending on what behavior you need to emulate and what it is you're asserting. Note that you do not have to fully implement all methods; stubs are fine. Implement what is needed for your test scenarios:

private class MockDashboard implements IDashboard { 
    public Boolean initialiseCalled = false; 
    public Boolean updateRPMsCalled = false; 
    public Integer updateRPMsCalledWith = null; 
    public void initialise() { initialiseCalled = true; } 
    public void updateRPMs(Integer rpms) {  
        updateRPMsCalled = true;  
        updateRPMsCalledWith = rpms;  
    } 
    public void off() { } 
} 
 
private class MockEngine implements IEngine { 
    public Boolean startCalled = false; 
    public void start() { startCalled = true; }     
    public void stop() { } 
    public Boolean isRunning() { return true; } 
} 
 
private class MockDisplay implements IDisplay { 
    public Boolean showMessageCalled = false; 
    public String showMessageCalledWithMessage = null; 
    public void backlight(Boolean onOff) { } 
    public void showMessage(
Integer positionX, Integer positionY, String message) { showMessageCalled = true; showMessageCalledWithMessage = message; } public String getMessageShowAt(
Integer positionX, Integer positionY) { return null; } public Boolean isVisible() { return false; } }
You can keep the implementation of these mock classes contained and scoped within your Apex test classes or use inner classes within a single class, if needed.  Additionally, the Salesforce DX folder structure is flexible and allows you to create sub-folders under the /classes folder, for example,/classes/tests/mocks.

After making the preceding changes and the introduction of the mocking classes, the implementation of the Car class object model now supports unit testing through CDI.

Each of the following unit tests resides in the corresponding test class within the sample code for this chapter, for example, CarTest, EngingeTest, and DashboardTest.

The following is a unit test for the Car.start method:

@IsTest 
private static void 
  whenCarStartCalledDashboardAndEngineInitialised () { 
         
   // Given 
   MockDashboard mockDashboard = new MockDashboard(); 
   MockEngine mockEngine = new MockEngine(); 
         
   // When 
   Car car = new Car(mockEngine, mockDashboard); 
   car.start(); 
         
   // Then 
   System.assert(car.isRunning()); 
   System.assert(mockDashboard.initialiseCalled); 
   System.assert(mockEngine.startCalled); 
} 
Notice the method naming approach used by the test method. This is deliberate, as unit testing often results in the creation of many small unit test methods. Having a naming convention such as the examples shown in this section helps make it clearer at a glance what the test is covering. Once again, this convention is borrowed from the BDD principles referenced earlier in this chapter.

Notice that the test did not need any setup to fulfill the setup requirements of the engine and dashboard dependencies, such as constructing an instance of the Display class.

Mock classes can also record the values passed as parameters for assertion later. The following is a unit test for the engine.start method:

    @IsTest 
 private static void whenStartCalledDashboardUpdated() { 
         
    // Given 
    MockDashboard mockDashboard = new MockDashboard(); 
         
    // When 
    Engine engine = new Engine(mockDashboard); 
    engine.start(); 
         
    // Then 
    System.assert(engine.isRunning());         
    System.assert(mockDashboard.updateRPMsCalled); 
    System.assertEquals(1000,
mockDashboard.updateRPMsCalledWith); }

It uses the mock implementation of the dashboard to confirm that the Dashboard.updateRPMs method was correctly passed the corresponding value. The following unit test is for the Dashboard.updateRPMs method:

@IsTest 
private static void whenUpdateRPMsCalledMessageIsDisplayed() { 
         
    // Given 
    MockDisplay mockDisplay = new MockDisplay();         
         
    // When 
    Dashboard dashboard = new Dashboard(mockDisplay); 
    dashboard.updateRPMs(5000); 
         
    // Then 
    System.assert(mockDisplay.showMessageCalled); 
    System.assertEquals('RPM:5000',
mockDisplay.showMessageCalledWithMessage); }

The preceding code uses a mock implementation of the Display class to test the correct interaction between the Dashboard and the Display class. The interaction being tested here is that the display text is correctly calculated and passed to the Display class when the updateRPMs method is called on the Dashboard class.

Using interfaces to achieve dependency injection has an overhead in the development and use of interfaces in the production code. This is something Salesforce has improved on with the introduction of the Apex Stub API, described later in this chapter. As the Stub API is still quite new, you may still see the DI accomplished with interfaces. The Apex Enterprise Patterns library utilized it prior to the arrival of the Stub API. Also keep in mind, as we have seen in previous sections of this book, that Apex interfaces also have many other uses in providing flexibility in your architecture's evolution and reducing coupling between clearly defined boundaries, such as those in this book and those your logic can also define.

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

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