Waldo sessions now support scripting! – Learn more
App Development

How to Use Mockito to Mock a Generic Class

Juan Reyes
Juan Reyes
How to Use Mockito to Mock a Generic Class
January 24, 2023
7
min read

This article will explore how to use the Mockito library to mock a generic class in Java. We will start by explaining what Mockito is and how to use it. Then we will explore the @Mock annotation in Java for mock tests. After that, we will delve into the particulars of using Mockito in a generic class. Finally, we will list the advantages and disadvantages of using a library like Mockito in Java.

Let's jump right in.

Mockito is a popular Java library that allows you to create mock objects for testing purposes

What Is Mockito?

Mockito is a popular Java library that allows you to create mock objects for testing purposes. Mocking is a technique that lets you replace the functionality of an object or class with a simulated version, which can help test how your code interacts with other components.

To mock a generic class with Mockito, you'll need to do the following:

Create a mock object of the generic class using the Mockito.mock() method.

    
      MyGenericClass<String> mock = Mockito.mock(MyGenericClass.class);
    

Set up the desired behavior of the mock object using Mockito's when() and thenReturn() methods.

    
      when(mock.getValue()).thenReturn("Hello, world!");
    

This will cause the mock object to return "Hello, world!" whenever the getValue() method is called.

Use the mock object in your test code like any other object.

    
      assertEquals("Hello, world!", mock.getValue());
    

Verify that the mock object was used as expected using the Mockito.verify() method.

    
      verify(mock).getValue();
    

This will ensure that the getValue() method is called exactly once on the mock object.

Using the @Mock Annotation

The @Mock annotation is a convenient way to create mock objects in Mockito. It allows you to create and inject mock objects into your test classes without manually calling the Mockito.mock() method.

To use the @Mock annotation, you'll need to do the following:

Annotate a field in your test class with @Mock.

    
      @Mock
      private MyClass mock;
    

This will create a mock object of the specified type and assign it to the field.

Initialize the mock objects in your test class using the MockitoAnnotations.initMocks() method.

    
      @Before
      public void setUp() {
        MockitoAnnotations.initMocks(this);
      }
    

This will initialize all fields in your test class annotated with @Mock and assign them their corresponding mock objects.

Use the mock object in your test methods as you would any other object.

    
      when(mock.doSomething()).thenReturn(true);
      assertTrue(mock.doSomething());
    

The @Mock annotation is a convenient way to create and manage mock objects in your tests and can save you time and boilerplate code when setting up mock objects.

Keep in mind that the @Mock annotation only creates mock objects. It does not set up their behavior. So you'll still need to use the when() and thenReturn() methods or another method of specifying the mock object's behavior, such as the Answer interface or a custom Answer implementation.

Using Mockito on a Generic Class

Using Mockito to mock a generic class can be more complicated than mocking a regular class due to the added complexity of generics. However, with a bit of practice and an understanding of how Mockito works, you should be able to use it to mock generic classes in your tests effectively.

One important thing to keep in mind when mocking generic classes is that Mockito's mock() method uses type erasure to create the mock object. This means that the type information of the generic class is not preserved at runtime, which can cause issues if you try to use the mock object in a way that relies on the specific type parameters of the class.

For example, consider the following generic class:

    
      public class MyGenericClass<T> {

          private T value;
        
          public MyGenericClass(T value) {
            this.value = value;
          }
        
          public T getValue() {
            return value;
          }
        }
    

If you create a mock object of this class using Mockito's mock() method, Mockito will replace the type parameter T with the type Object at runtime. Unfortunately, if you try to call a method on the mock object that takes an argument of type T, you'll get a compile-time error because the mock object doesn't have a corresponding method that takes an Object argument.

To work around this issue, you can use Mockito's @Captor annotation to create an ArgumentCaptor object that can capture the arguments passed to the mock object's methods. You can then use the ArgumentCaptor to access the captured arguments and verify that they have the expected type.

For example:

    
      @Captor
      private ArgumentCaptor<T> captor;
      when(mock.setValue(any())).thenReturn(true);
      mock.setValue("Hello, world!");
      verify(mock).setValue(captor.capture());
      assertEquals("Hello, world!", captor.getValue());
    

This code sets up the mock object to return true whenever the setValue() method is called with any argument and then verifies that the setValue() method was called with the correct argument using the ArgumentCaptor.

Mocking Using the Answer Interface

Another option for mocking generic classes is to use Mockito's Answer interface, which allows you to specify a custom behavior for the mock object's methods. This can be useful if you need to perform more complex operations or use the type information of the generic class in your mock object's behavior.

For example:

    
      when(mock.getValue()).thenAnswer(new Answer<T>() {
          public T answer(InvocationOnMock invocation) throws Throwable {
            T value = (T) invocation.getArguments()[0];
        
            return value;
          }
        });
    

This code sets up the mock object to return the argument passed to the getValue() method as the return value.

To use the Answer interface, you'll need to create a new implementation of the Answer interface and override the answer() method. The answer() method takes an InvocationOnMock object as an argument, representing the method call made on the mock object. You can use the InvocationOnMock object to access the arguments passed to the method and perform any desired operations.

Here's an example of how you might use the Answer interface to mock a generic class:

    
      public class MyAnswer implements Answer<T> {
          public T answer(InvocationOnMock invocation) throws Throwable {
            // Access the arguments passed to the method
            Object[] args = invocation.getArguments();
        
            T arg = (T) args[0];
            // Perform any desired operations on the argument
            T transformedArg = transform(arg);
            // Return the transformed argument
            return transformedArg;
          }
        }
    

You can then use this Answer implementation to set up the desired behavior of your mock object like this:

    
      when(mock.getValue()).thenAnswer(new MyAnswer());
    

This will cause the mock object to return the transformed argument whenever the getValue() method is called.

Mockito has a simple and intuitive API that makes it easy to create and use mock objects in your tests

Mockito's Advantages and Disadvantages

Here are some advantages of using Mockito:

  • It's easy to use. Mockito has a simple and intuitive API that makes it easy to create and use mock objects in your tests.
  • It's widely used. Mockito is a popular library with a large user base, which means it's well tested and has a wealth of documentation and resources available.
  • It integrates well with other tools. Mockito works well with other testing tools and libraries, such as JUnit and PowerMock.
  • It supports a variety of mock object types. Mockito allows you to create mock objects of various types, including regular classes, interfaces, and final classes.

Some potential disadvantages of using Mockito include the following:

  • Mocking generic classes: As mentioned earlier, Mockito's mock() method uses type erasure, making it difficult to mock generic classes in a way that preserves their type information.
  • Mocking final classes and methods: Mockito cannot mock final classes or methods, which can be a limitation if you need to test code that relies on these classes or methods.
  • Mocking static methods: Mockito does not directly support the mocking of static methods, although this can be done using the PowerMock library in combination with Mockito.
  • Mocking complex behavior: While Mockito is good at mocking simple behavior, it can be more challenging to mock complex or nuanced behavior, which may require more manual setup or custom Answer implementations.

Moving On

Mockito is a powerful tool for creating mock objects in Java to mock generic and regular classes. By using the when() and thenReturn() methods, the @Captor annotation, and the Answer interface, you can set up the desired behavior of your mock objects. This will allow you to test your code in various situations.

However, I advise you to check out Waldo's extensive toolkit for UI testing if you want to make sure that your code is reliable and safe. Even for nondevelopers, it is incredibly accessible and doesn't require any code.

Automated E2E tests for your mobile app

Waldo provides the best-in-class runtime for all your mobile testing needs.
Get true E2E testing in minutes, not months.

Reproduce, capture, and share bugs fast!

Waldo Sessions helps mobile teams reproduce bugs, while compiling detailed bug reports in real time.