2016. március 25., péntek


How to override a function in Mockito. 


So I want not just the return value to be faked, but it should do something completely different as the implementation of the java class, I am mocking. To be concrete, I wanted to add an element into an underlying list in the process() method, instead of let the processing done. At the end of the test method I wanted to check the content of the list, while I wanted to know, how many element has been added in the class under test.

In JMockit it is not a problem at all, while it works exactly like this:


new MockUp<DBManager>() {
   @SuppressWarnings("unused")
   @Mock
   public String retrieveAccountHolderName(int accountId ){
     myList.add(new Integer(accountId)); // myList is declared in the test method
     return "Abhi";
   }
};

So basically you can do whatever you want inside of the mocked method.


To make the problem even harder, I had to inject the mocked object into a service class, while I needed to test this class after all.

Solution:

  • I have created a subclass of AcitveDirectoryDataImporter which overrides the process() method.
  • Defined it as mocked object
  • Let it used by the ActiveDirectoryReaser, using the @InjectMocks annotation
  • Instructed Mockito to use the original process method of MockedAcitveDirectoryDataImporter
  • I also had to create a method, reaching the added elements inside of the MockedAcitveDirectoryDataImporter, and let Mockito to use it's original implementation.

Known limitations:
  1. In case of AcitveDirectoryDataImporter final, the solution will not work. 
  2. The list, containing processed entities had to be declared inside of the new class. It is not possible the class to reach variables of the test class. It is due to the way how Mockito creates the mocked objects. 
Code snippet of the solution:


/**
 * Test class for {@link ActiveDirectoryDataReader}
 * 
 * @author Peter Varga
 *
 */
@RunWith(MockitoJUnitRunner.class)
public class ActiveDirectoryDataReaderTest {

 @Mock
 AdPropertiesHolder propertiesHolder;

 @Mock
 MockedAcitveDirectoryDataImporter acitveDirectoryDataImporter = new MockedAcitveDirectoryDataImporter();

 @Mock
 LdifFileHandlerFactory opcoFileHandlerFactory;

 @InjectMocks
 ActiveDirectoryDataReader activeDirectoryDataReader;

 private void initMockedDependencies(String importDirectoryPath) {
  // Set application properties for test
  when(propertiesHolder.getImportDirectoryPath()).thenReturn(importDirectoryPath);
  when(propertiesHolder.isKeepMarkerFiles()).thenReturn(true);

  // set behavior of mocked objects
  // call original methods of MockedAcitveDirectoryDataImporter
  doCallRealMethod().when(acitveDirectoryDataImporter).process(any(ActiveDirectoryUser.class));
  doCallRealMethod().when(acitveDirectoryDataImporter).getProcessedUsers();
  doCallRealMethod().when(opcoFileHandlerFactory).getInstance(any(String.class));
 }

 @Test
 public void testFindAllWithMultipleFiles() throws Exception {
  String importDirectoryPath = "./src/test/resources/at/a1ta/eap/avatar/dataimport/ad/file/multiple_files";
  initMockedDependencies(importDirectoryPath);

  // call processing method
  activeDirectoryDataReader.findAll();

  List<ActiveDirectoryUser> processedUsers = acitveDirectoryDataImporter.getProcessedUsers();
  Assert.assertEquals(8230, processedUsers.size());
 }

 ... // more test methods here


 /*
  * Class to override default process behavior for AcitveDirectoryDataImporter. It simply adds the ActiveDirectoryUser objects to a list instead of
  * processing them.
  */
 class MockedAcitveDirectoryDataImporter extends AcitveDirectoryDataImporter {
  private List<ActiveDirectoryUser> processedUsers = new ArrayList<>();

  List<ActiveDirectoryUser> getProcessedUsers() {
   if (processedUsers == null) {
    processedUsers = new ArrayList<>();
   }
   return processedUsers;
  }

  @Override
  public void process(ActiveDirectoryUser adUser) {
   getProcessedUsers().add(adUser);
  }
 }

Nincsenek megjegyzések:

Megjegyzés küldése