Published on

Understanding Mockito and PowerMockito

Mockito
Table of Contents

Introduction

Mockito - is a mocking framework. it simulates the behavior of a real method/object.

Why do we need a mocking framework ? >> We want to test the functionality of a class in isolation in a controlled way. However, there are several obstacles

  • Real objects can be slow (like database connection).
  • Objects contain lots of dependencies and these are often complex to configure and instantiate.
  • Sometimes, the code may not be coded yet and only interface exists.
  • And if the code results into side effects - like code sending an email upon invocation.

In this situations, we want someone to imitate the exact same behavior our real method/objects possess without ever executing the original method.
This is exactly what Mockito does.

Understanding concept

1. Diagram overview

Mockito steps diagram overview

2. Terms and concepts

@InjectMocks:
The Class under test and inject the mock object into it.

@Mock:
Mock this dependency for the class that is under test.

Stubbing:
Return a given value when a specific method is called.

  • Act as a replacement to real code
  • Fake the behavior of code not yet developed
  • simulate controlled environment
  • Record information about method calls and also verify call

Stubbing example - Mockito.when() in conjunction with: eg: when("method()").thenReturn("value");

  • thenReturn(..)
  • thenThrow(..)
  • thenCallRealMethod(..)
  • thenAnswer(..)

Spies:
It Behaves in the same way as a normal instance and allow us to actually call all the real methods of the object while still tracking every interaction.
Mockito.spy or @Spy

Assert:
Check or assure that the method call returned expected value.

  • assertEquals(expected, actual)
  • assertNotNull()
  • assertThrows()
  • assertTrue ...

Verify:
We use Mockito.verify(..) method to check whether an intended mock operation was called or not.

  • times(n)
  • atLeastOnce()
  • atLeast(n)
  • atMost(n)
  • never() ...

More Mockito Features:

  • Argument matchers
  • Verification Order
  • Argument capturing

Argument matchers:

Mockito.when( userRepository.findByCity(Matchers.anyString())).thenReturn(user);
// eq(), anyInt(), anyDouble(), contains(), startsWith()

Verification Order:

Mockito.inOrder()

Argument capturing:

// @Captor private ArgumentCaptor<Person> personCaptor;
// OR
ArgumentCaptor<Person> personCaptor= ArgumentCaptor.forClass(Person.class);
// -----------------------------------
verify(mockObject).doSomething(personCaptor.capture());
assertEquals("John", personCaptor.getValue().getName());

3. Code Overview

Mockito steps code sample

Code implementation
Include Dependency (in "pom.xml" or build.gradle) —

org.mockito.mockito-core*
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.Optional;

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

// Use @RunWith(MockitoJUnitRunner.class) Junit 4 or @ExtendWith(MockitoExtension.class) if Junit 5
// Initializes the mocks annotated with @Mock and injects them into the class annotated with @InjectMocks.
public class UserServiceImplTest {

    // @Mock: Creates a mock instance of UserRepository to simulate its behavior without relying on the actual implementation.
    @Mock
    private UserRepository userRepository;

    // @InjectMocks: Creates an instance of UserServiceImpl and injects the mocked UserRepository into it.
    @InjectMocks
    private UserServiceImpl userServiceImpl;

    private User user;

    @BeforeEach
    void setUp() {

        // User object to be used in the test.
        user = new User(1L, "Test Name", "test123", 1);
    }

    @Test
    void testGetUserById() {
        // Stubbing:
        // Defines the behavior of the mocked userRepository when findById is called with user.getId().
        // It returns an Optional containing the user object, avoiding the actual call
        when(userRepository.findById(user.getId())).thenReturn(Optional.of(user));

        // Act: Calls the method under test, which should use the mocked userRepository.
        User foundUser = userServiceImpl.getUserById(1L);

        // Assert:
        // Verifies that the returned user is equal to the expected user object.
        assertEquals(user, foundUser, "The returned user should match the mocked user.");

        // Verify:
        // Ensures that the findById method was called exactly once with the specified argument.
        verify(userRepository, times(1)).findById(1L);
    }
}

Code Git repo: ashrawan/crud-jpa-with-test
Official Site: Official Mockito Site


PowerMockito

Mockito create mock object using proxy pattern.

Since proxy pattern is created by extending it, "mockito cannot mock static, private, final methods, and constructor.

The bytecode manipulation has to be done at runtime to mock such methods. So we use library called PowerMockito which create mock object using proxy pattern and add further behaviour to provide additional facilities to mock these methods.

Test we want to test:

@Service
public class UserServiceImpl implements User {

  // constructor
  public UserServiceImpl() {
      System.out.println("Spy Instantiate real object, constructor called");
  }

  public String getPublicValue() {
      return "Hello";
  }

  private int getSome(int a, int b) {
      return a+b;
  }

  @Override
  public int getSecret() {
      return getSome(1, 2);
  }

  public static String getClassCode() {
      return "User000";
  }

  public final String getInfo(){
      return "final method";
  }

}

Test Class:

@RunWith(PowerMockRunner.class)
@PrepareForTest(UserServiceImpl.class)
public class UserServiceTest {

}

Static Method:

@Test
public void testStaticMethod(){

mockStatic(UserServiceImpl.class);
when(UserServiceImpl.getClassCode()).thenReturn("User000");
assertEquals("User000", UserServiceImpl.getClassCode());
}

Private Method:

@Test
public void testPrivateMethod() throws Exception{

UserServiceImpl userServiceImplSpy = PowerMockito.spy(new UserServiceImpl());
when(userServiceImplSpy, "getSome", anyInt(), anyInt()). thenReturn(30);
assertEquals(30, userServiceImplSpy.getSecret());
}

Final Method:

@Test
public void testFinalMethod() throws Exception{

UserServiceImpl userServiceImplMock = PowerMockito.mock(UserServiceImpl.class);
when(userServiceImplMock.getInfo()).thenReturn("final method");
assertEquals("final method", userServiceImplMock.getInfo());
}

Constructor:

@Test
public void testConstructor() throws Exception{

UserServiceImpl userServiceImplMock = mock(UserServiceImpl.class);
whenNew(UserServiceImpl.class).withNoArguments().thenReturn(userServiceImplMock);
UserServiceImpl userServiceImp = new UserServiceImpl();
verifyNew(UserServiceImpl.class).withNoArguments();
}

Public Method:

@Test
public void testPublicMethod() throws Exception{

UserServiceImpl userServiceImplMock = mock(UserServiceImpl.class);
when(userServiceImplMock.getPublicValue()).thenReturn("Hello");
assertEquals("Hello", userServiceImplMock.getPublicValue());
}

GitHub: ashrawan/Demo-PowerMockito

Reference - Official site: Official PowerMockito Site