Test Driven Development using JUnit5 and Mockito


Test-Driven Development (TDD) is a software development approach where tests are written before the actual code. TDD has gained substantial traction due to its emphasis on code quality and maintainability. This article explores how TDD can be effectively implemented using JUnit5 and Mockito, two powerful frameworks in the Java ecosystem.

What is Test-Driven Development?

Test-Driven Development is an iterative development process where developers first write a test case for a new function or feature, then write the minimum amount of code to pass that test, and finally refactor the code for optimization. This approach enhances the design, reduces bugs, and improves the overall maintainability of the code.

Understanding JUnit5 and Mockito

JUnit5 is the latest version of JUnit, a widely-used testing framework in Java for unit testing. It introduces several new features and improvements over its predecessor, enhancing the ease and flexibility of testing in Java.

Mockito, on the other hand, is a mocking framework that assists in creating mock objects and defining their behavior, which is incredibly useful for testing code with external dependencies.

Test-Driven Development with JUnit5 and Mockito

Step 1: Writing a Failing Test

The first step in TDD is to write a failing test. For this, we define a method annotated with @Test in JUnit5 −

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class ExampleTest {

   @Test
   void shouldReturnTrueWhenNumberIsEven() {
       Example example = new Example();
       boolean result = example.isNumberEven(3);
       assertTrue(result);
   }
}

This test will initially fail as we haven't implemented the isNumberEven method in the Example class yet.

After having a failing test, we write just enough code to pass the test. For instance −

class Example {

   boolean isNumberEven(int num) {
       return num % 2 == 0;
   }
}

Running the test again, we should see it pass as the isNumberEven method now correctly checks if a number is even.

Step 3: Refactor the Code

The last step involves refining and optimizing the code without changing its behavior. Although our example is quite simple and might not need refactoring, in more complex scenarios, this step could involve reducing redundancy, improving readability, or optimizing performance.

Mocking with Mockito

In scenarios where the method to be tested has external dependencies, Mockito is used to create mock objects. This allows for isolating the method for testing −

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

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

class ExampleTest {

   @Test
   void shouldCallDependency() {
       // Arrange
       Dependency dependency = Mockito.mock(Dependency.class);
       Example example = new Example(dependency);

       // Act
       example.performAction();
		
       // Assert
       verify(dependency, times(1)).action();
   }
}

In this test, we create a mock Dependency object and verify that its action method is called once when we call performAction on the Example object.

Conclusion

Test-Driven Development, coupled with JUnit5 and Mockito, offers a robust foundation for writing high-quality, maintainable code. By understanding the TDD cycle and effectively using these frameworks, developers can significantly enhance their testing skills and code quality.

Updated on: 19-Jun-2023

281 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements