In the world of software development, unit testing is essential for ensuring code quality and efficiency. TestNG, Mockito, and Hamcrest Matchers are powerful tools that, when used together, create robust and maintainable unit tests. In this guide, we will walk you through a step-by-step process to create a TestNG test with Mockito and Hamcrest Matchers.

Prerequisites

Before we begin, ensure you have the following:

  1. Java Development Kit (JDK) installed
  2. An Integrated Development Environment (IDE) like IntelliJ IDEA or Eclipse
  3. Maven or Gradle as your build tool

Dependencies

Add the following dependencies to your Maven or Gradle project:

Maven:

<dependencies>
  <dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>7.4.0.0</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.1.0</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest</artifactId>
    <version>2.2</version>
    <scope>test</scope>
  </dependency>
</dependencies>

Gradle:

dependencies {
    testImplementation 'org.testng:testng:7.4.0'
    testImplementation 'org.mockito:mockito-core:4.1.0'
    testImplementation 'org.hamcrest:hamcrest:2.2'
}

Writing a TestNG Test with Mockito and Hamcrest Matchers

For this tutorial, let’s assume we are testing a simple UserService class that has a UserRepository dependency.

Step 1: Create a UserService interface

public interface UserService {
    User getUserById(String id);
}

Step 2: Implement the UserService interface

public class UserServiceImpl implements UserService {
    private UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public User getUserById(String id) {
        return userRepository.findById(id);
    }
}

Step 3: Create a UserRepository interface

public interface UserRepository {
    User findById(String id);
}

Step 4: Implement the UserRepository interface

public class UserRepositoryImpl implements UserRepository {
    // Assume the implementation is connecting to a database and fetching the user by id.
    @Override
    public User findById(String id) {
        // Implementation goes here.
        return null;
    }
}

Step 6: Create a TestNG test class with Mockito and Hamcrest

Create a new test class called UserServiceTest in the appropriate test folder of your project.

import org.mockito.Mockito;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

public class UserServiceTest {

    private UserService userService;
    private UserRepository userRepository;

    @BeforeMethod
    public void setUp() {
        userRepository = Mockito.mock(UserRepository.class);
        userService = new UserServiceImpl(userRepository);
    }

    @Test
    public void getUserById_success() {
        // Arrange
        String userId = "123";
        User expectedUser = new User(userId, "John Doe");
        Mockito.when(userRepository.findById(userId)).thenReturn(expectedUser);

        // Act
        User actualUser = userService.getUserById(userId);

        // Assert
        Mockito.verify(userRepository).findById(userId);
        assertThat(actualUser, equalTo(expectedUser));
    }
}

Explanation:

1. Import the necessary Mockito and Hamcrest classes at the beginning of the test class:

import org.mockito.Mockito;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

2. In the setUp() method, which is annotated with @BeforeMethod, create a mock of the UserRepository class and instantiate the UserServiceImpl with the mocked repository. This method runs before each test case:

private UserService userService;
private UserRepository userRepository;

@BeforeMethod
public void setUp() {
    userRepository = Mockito.mock(UserRepository.class);
    userService = new UserServiceImpl(userRepository);
}

3. Create a test method called getUserById_success() and annotate it with @Test:

@Test
public void getUserById_success() {
    // Arrange
    String userId = "123";
    User expectedUser = new User(userId, "John Doe");

    // Act
    User actualUser = userService.getUserById(userId);

    // Assert
    Mockito.verify(userRepository).findById(userId);
    assertThat(actualUser, equalTo(expectedUser));
}

4. In the Arrange phase, set up the test data and the expected behavior of the mocked UserRepository by using Mockito’s when() and thenReturn() methods:

String userId = "123";
User expectedUser = new User(userId, "John Doe");
Mockito.when(userRepository.findById(userId)).thenReturn(expectedUser);

5. In the Act phase, call the getUserById() method on the userService instance:

User actualUser = userService.getUserById(userId);

6. In the Assert phase, use Mockito’s verify() method to ensure that the findById() method of the UserRepository was called with the correct argument. Then, use Hamcrest’s assertThat() method along with the equalTo() matcher to compare the expected and actual user objects:

Mockito.verify(userRepository).findById(userId);
assertThat(actualUser, equalTo(expectedUser));

Conclusion

In this guide, we have shown you how to create a TestNG test using Mockito for mocking and Hamcrest Matchers for assertions. By following these steps, you can create efficient and maintainable unit tests for your Java applications, ensuring the quality