Mockito Framework: How it works

Reading Time: 3 minutes

Introduction

Mockito is a popular mocking framework that is useful to test the written code. It enables to write the essential test cases for real-world projects and applications.

Unit Testing: It is a software testing technique to test the unit of a code that can be logically isolated in a system.

Unit: It is referred to as an individual functionality of a program.

Mocking: It is a process of creating mock or clone objects which act as dummy objects to the real objects. When we develop the applications, we develop them into layers.

Diagram: Showing the structure of the application: Web layer, Business layer, and Data layer

Need for Mocking and Mockito Framework

Some of the points defined below illustrate the need for Mockito Framework:

  • In case project is under developement, and we want test to the single component that is dependent on an another component which is incomplete. Then we can use mocking for writting the test cases.
  • When we need to test the database operations wich are the working fast enough to meet the requirements. Then also we can mock the required objects to test the read/write operations using mocking.

Let’s see with an example of why we need the Mockito framework and how we can use it to test the written code:

Real Class

public class SampleClass {

    public int doSummation(int [] input) {

        int sum =0;
        for(int val: input) {
            sum += val;
        }
        return sum;
    }

}

Test Class

@ExtendWith(MockitoExtension.class)
public class SampleClassTest {

    @Test
    public void doSummationWhenInputIsValid() {

        SampleClass sampleObject = new SampleClass();

        int actualOutput = sampleObject.doSummation(new int[] {10, 20, 30});
        int expectedOutput = 60;

        assertEquals(expectedOutput, actualOutput);
    }

    @Test
    public void doSummationWhenInputIsEmpty() {

        SampleClass sampleObject = new SampleClass();

        int actualOutput = sampleObject.doSummation(new int[] {});
        int expectedOutput = 0;

        assertEquals(expectedOutput, actualOutput);
    }

}

Some important annotations provided by Mockito Framework:

@Mock Annotation

 This annotation helps to mock the real objects that reduce the repetition of mock objects. It enhances the readability of the test cases as the parameters. The @Mock annotation is available in the org.mockito package.

@Mock
ServiceClassName mockObjectName;  
 @Test
 public void mocking() {
        
    ArrayList mockArrayList = mock(ArrayList.class);
    mockArrayList.get(0); // null
    mockArrayList.size(); // 0
        
    mockArrayList.add("Mock1");
    mockArrayList.add("Mock2");
    System.out.println(mockArrayList.size()); // 2
        
    when(mockArrayList.size()).thenReturn(5);

    System.out.println(mockArrayList.size()); // 5
 }

@RunWith Annotation

This is a class-level annotation, allows to keep the test clean and to improve debugging. It also detects the unused stubs available in the test and initializes mocks annotated with @Mock annotation. The @RunWith annotation is available in the org.mockito.junit package.

@RunWith(MockitoJUnitRunner.class)  
public class SeviceClassName {  
 
}  

@InjectMocks Annotation

This annotation marks a field or parameter on which the injection should be performed. It also allows short-hand mock and spy injections and minimizes the repetitive mocks and spy injection. The @InjectMocks annotation is available in the org.mockito package.

@InjectMocks  
ServiceClassName mockObjectName;  

@Captor Annotation

This annotation allows the creation of a field-level argument captor. And we can use Mockito’s verify() method to get the values passed when a method is called. The @Captor annotation is available in the org.mockito package.

@Captor  
ArgumentCaptor<String> argumentCaptor;  

@Spy Annotation

This annotation enables the creation of partially mock objects. That means it can wrap the field instances in a spy object. The @Spy annotation is available in the org.mockito package.

@Spy      
ArrayList<String> spyArrayList;  
@Test
public void spying() {

    ArrayList spyArrayList = spy(ArrayList.class);

    spyArrayList.add("Spy1");
    System.out.println(spyArrayList.get(0)); // Spy1
    System.out.println(spyArrayList.size()); // 1

    spyArrayList.add("Spy2");
    spyArrayList.add("Spy3");

    System.out.println(spyArrayList.size()); // 3

    when(spyArrayList.size()).thenReturn(10);

    System.out.println(spyArrayList.size()); // 10
 }

Example for bad practice v/s good practice

This is an example to showcase the difference between good and bad practices for writing test cases that enhance the readability of the test cases.

Bad Practice:

@Test
public void badTestExample() {

    List<Product> products = new ArrayList<Product>();

    products.add(new ProductImp(230, "Product123", ProductType.COSMETIC, new 
    AmountImp(new 
    BigDecimal("2.0"), Currency.INDIAN)));
    products.add(new ProductImp(523, "Product245", ProductType.COSMETIC, new 
    AmountImp(new 
    BigDecimal("7.0"), Currency.INDIAN)));

    Amount amt = clientDetails.getClientProductsSum(products);

   assertEquals(Currency.INDIAN, amt.getCurrency);
   assertEquals(new BigDecimal("9.0").INDIAN, amt.getValue);
}

Good Practice

@Test
public void goodTestExample() {

    Amount[] amounts = {
            new AmountImp(new BigDecimal("4.0"), Currency.INDIAN);
            new AmountImp(new BigDecimal("7.0"), Currency.INDIAN);
    };

    Amount expectedAmount = new AmountImp(new BigDecimal("11.0", Currency.INDIAN));

    List<Book> books = createBooksListWithAmount(amounts);

    Amount actualAmount = clientDetails.getClientBooksSum(books);

    assertEquals(actualAmount, expectedAmount);
}

Reference

For more detailed information about the Mockito framework, please click here.