Junit: Init methods

Mockito Annotations

  • @Mock: Creates a dummy implementation for an interface or a class in which you define the output of certain method calls. Mock objects are configured to perform a certain behavior during a test. This is not a real object and does not maintain the state changes to it.
  • @Spy: Creates a real instance of the class and track every interactions with it. It maintains the state changes to it.
  • @Captor: Creates an ArgumentCaptor instance which is used to capture method argument values for further assertions.
  • @InjectMocks: Create the object of class to be tested and than insert it’s dependencies (mocked) to completely test the behavior. 
@RunWith(MockitoJUnitRunner.class)
public class ExampleTest {
 
    @Mock
    HashMap<String, Integer> hashMap;
 
    @Captor
    ArgumentCaptor<String> keyCaptor;
 
    @Captor
    ArgumentCaptor<Integer> valueCaptor;
 
    @Spy
    @InjectMocks
    Example example;
    
    @Before
    public void setUp() {
       MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testMethod() {
        // Given
        // When
        hashMap.put("A", 10);
        // Then
        Mockito.verify(hashMap).put(keyCaptor.capture(), valueCaptor.capture());
        assertEquals("A", keyCaptor.getValue());
        assertEquals(new Integer(10), valueCaptor.getValue());
    }
}

Source

https://martinfowler.com/bliki/TestDouble.html

Mock manually

public class ExampleTest {
   @Before
   public void setUp() {
      // Real instance
      Sample sample = new Sample();
      // Mocked instance
      Sample sampleMocked = mock(Sample.class);
   }
}

Understanding mocks

Mock objects are configured to perform a certain behavior during a test.

This is not a real object and does not maintain the state changes to it.

public class ExampleTest {

   @Before
   public void setUp() {

      // Real instance
      Sample sample = new Sample();
      // Mocked instance
      Sample sampleMocked = mock(Sample.class);

      /**
      * Fields
      */

      // This will set value field to 2
      sample.value = 2; // sample.value = 2
      // This actually will NOT set value field to 2, it will be still its default value
      sampleMocked.value = 2; // sampleMocked.value = 0

      /**
      * Methods
      */

      // This call will run the real method and return a value according to the inputs
      int result = sample.sum(1, 1); // result = 2
      // This call will NOT run the real method and will return a default value according to the result type
      int resultMocked = sampleMocked.sum(1, 1); // resultMocked = 0

   }
}

Mocks allow us to focus in the tests of the a class, without taking into account the logic in other methods. For example:

Class to test:

public class Example {

   @Autowired
   private ARepository aRepository;

   public String getById(int id) {
      // Retrieves the object with the given ID from the database.
      String obj = this.aRepository.findById(id).get();
      // Some random logic
      if(obj.equals("Not found")) {
         return "Invalid ID";
      }
      return obj;
   }

}

Test:

@RunWith(MockitoJUnitRunner.class)
public class ExampleTest {
 
    @Mock
    private ARepository aRepository;
 
    @Spy
    @InjectMocks
    Example example;
    
    @Before
    public void setUp() {
       MockitoAnnotations.initMocks(this);
    }

    @Test
    public void test_getById_when_validID_then_returnsObject() {
        // Given
        int id = 5;
        // When
        String result = getById(id); // result = null
        /* getById will call aRepository.findById
            but since it's a mock, 
            it won't really get the object from the database,
            it won't do anything actually and it will just return a default value
            (since it's a String, it will return null).
            This allows us to test methods without making real calls to the database.
        */
        // Then
        // This will verify that we did call the desired methods.
        Mockito.verify(aRepository).findById(eq(id));
    }
}

Mocks also allow us to mock some classes and force desired outputs. For example:

@RunWith(MockitoJUnitRunner.class)
public class ExampleTest {
 
    @Mock
    private ARepository aRepository;
 
    @Spy
    @InjectMocks
    Example example;
    
    @Before
    public void setUp() {
       MockitoAnnotations.initMocks(this);
    }

    @Test
    public void test_getById_when_validID_then_returnsObject() {
        // Given
        int id = 5;
        String obj = "Not found";
        Mockito.when(aRepository.findById(eq(id))).thenReturn(obj);
        // When
        String result = getById(id);
        // Then
        Mockito.verify(aRepository).findById(eq(id));
        assertEquals("Invalid ID", result);
    }
}

Mock static methods

@RunWith(PowerMockRunner.class)
@PrepareForTest({Example.class, ExampleStatic.class}) // Mock the class under test and the classes with static methods we want to mock.
public class ExampleTest {
   ...
   @Before
   public void setUp() {
      PowerMockito.mockStatic(ExampleStatic.class);
      // Mock method
      when(ExampleStatic.method(...)).return(...);
      // Mock constructor
      PowerMockito.whenNew(ExampleStatic.class).withArguments(...).thenReturn(...);
      ...
   }
}

Other annotations

/*
Defer the loading of the classes listed in the annotation.
*/
@PowerMockIgnore({"javax.managment.*"}) 
/*
Suppress static initializers.
*/ 
@SuppressStaticInitialization({"javax.managment.RuntimeMXBean"})
public class ExampleTest {

}

@RunWith(PowerMockRunner.class) vs @RunWith(MockitoJUnitRunner.class)

Try to use always Mockito framework instead of PowerMockito one.

PowerMockito should be used as a last resort.

Link:

https://stackoverflow.com/questions/38268929/runwithpowermockrunner-class-vs-runwithmockitojunitrunner-class