Tuesday, September 25, 2018

Mockito: Mocking Final Methods With PowerMockito

Introduction

Mockito makes the testing job easier. But Mockito itself has its own limitations.

Poblem with Mocking final Method

One of such limitations is mocking of final method. When we try to mock a final method, as we normally mock other methods, it is not mocke. Real implementation of that method is executed. In fact, Mockito does not even complains about it. The real implementation is run without any Warning or Exception.

  /* File:    JobsTest.java
 * Created: 9/24/2018
 * Author:  Sabbir Manandhar
 *
 * Copyright (c) 2018 Hogwart Inc.
 */

package com.hogwart;


import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.modules.junit4.PowerMockRunner;

import java.util.ArrayList;
import java.util.List;

//=======================================================================================


/**
 * @author Sabbir Manandhar
 * @version 1.0
 */
@RunWith(PowerMockRunner.class)
public class JobsTest {

  //-------------------------------------------------------------------------------------

  @Test
  public void testGetJobCount() {
    List expectedJobIds = new ArrayList();
    expectedJobIds.add("J456");
    expectedJobIds.add("J459");

    Jobs jobs = Mockito.mock(Jobs.class);
    Mockito.doReturn(expectedJobIds).when(jobs).getJobIds();

    List jobIds = jobs.getJobIds(); // Here we expect to receive mocked Job IDs
    Assert.assertEquals(expectedJobIds.size(), jobIds.size());
  } // testGetJobCount

} // JobsTest

//=======================================================================================

/**
 * Class Representing Jobs Queue
 */
class Jobs {

  //-------------------------------------------------------------------------------------

  /**
   * Retrieves List of Job IDs and return
   * @return List of Job IDs
   */
  public final List getJobIds() {
    List result = new ArrayList();

    // ...
    // Implementations to compute list of job ids
    // ...

    return result;
  } // getJobIds

  // Other Methods

} // Jobs

      
Here the test will fail because we are expecting mocked job ids of size 2, but the actual implementation is run and an empty list is returned.

Solution - PowerMockito

Even though Mockito cannot mock final methods, they can be mocked using PowerMockito.

We need to use following in our code to fix our test.

  
@RunWith(PowerMockRunner.class)
@PrepareForTest(Jobs.class)
public class JobsTest {

  ...

    Jobs jobs = PowerMockito.mock(Jobs.class);
    PowerMockito.doReturn(expectedJobIds).when(jobs).getJobIds();

      
Following is the complete working solution:


  
/* File:    JobsTest.java
 * Created: 9/24/2018
 * Author:  Sabbir Manandhar
 *
 * Copyright (c) 2018 Hogwart Inc.
 */

package com.hogwart;


import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.util.ArrayList;
import java.util.List;

//=======================================================================================


/**
 * @author Sabbir Manandhar
 * @version 1.0
 */
@RunWith(PowerMockRunner.class)
@PrepareForTest(Jobs.class)
public class JobsTest {

  //-------------------------------------------------------------------------------------

  @Test
  public void testGetJobCount() {
    List expectedJobIds = new ArrayList();
    expectedJobIds.add("J456");
    expectedJobIds.add("J459");

    Jobs jobs = PowerMockito.mock(Jobs.class);
    PowerMockito.doReturn(expectedJobIds).when(jobs).getJobIds();

    List jobIds = jobs.getJobIds(); // Here we expect to receive mocked Job IDs
    Assert.assertEquals(expectedJobIds.size(), jobIds.size());
  } // testGetJobCount

} // JobsTest

//=======================================================================================

/**
 * Class Representing Jobs Queue
 */
class Jobs {

  //-------------------------------------------------------------------------------------

  /**
   * Retrieves List of Job IDs and return
   * @return List of Job IDs
   */
  public final List getJobIds() {
    List result = new ArrayList();

    // ...
    // Implementations to compute list of job ids
    // ...

    return result;
  } // getJobIds

  // Other Methods

} // Jobs


      

Note

If you spy a class and mock a final method, then you might get some exception messages when you try to run the test class. One of possible exception is org.mockito.exceptions.misusing.WrongTypeOfReturnValue.






No comments:

Post a Comment