Writing Unit Tests with JUnit and Mockito in spring boot
This guide provides a detailed walkthrough for writing unit tests with JUnit and Mockito. Here we’ll discuss the dependencies required, structure of the test case, and the step-by-step approach to write the test. In this particular example, we’ll be testing a method that creates a user. Writing Unit Tests with JUnit and Mockito in spring boot.
Required Dependencies
To write and run the unit tests, you need to include the following dependencies in your build.gradle:
//Spring test
testImplementation ‘org.springframework.boot:spring-boot-starter-test’
testImplementation ‘org.springframework:spring-test’
// JUnit 5
testImplementation ‘org.junit.jupiter:junit-jupiter-api:5.7.2’
testImplementation ‘org.junit.jupiter:junit-jupiter-engine:5.7.2’
// Mockito
testImplementation ‘org.mockito:mockito-core:4.1.0’
testImplementation ‘org.mockito:mockito-junit-jupiter:4.1.0’
// ByteBuddy (required by Mockito for creating mocks)
testImplementation ‘net.bytebuddy:byte-buddy:1.11.16’
Test Class Structure
Our test class UserProfileServiceTest is responsible for testing the methods inside the UserProfileService class.
The class starts with the @ExtendWith(MockitoExtension.class) annotation which instructs JUnit 5 to enable Mockito’s annotation-driven capabilities.
We create mocks for dependencies using the @Mock annotation, and the @InjectMocks annotation is used to create an instance of the class that needs to be tested (UserProfileService in this case) and inject the mocks as dependencies.
Setting up the Test Case
The @BeforeEach annotation is used to specify a method that should be executed before each @Test method in the class. Here we use MockitoAnnotations.initMocks(this) to initialize objects annotated with @Mock for each test method.
Writing the Test Case
Next, we have the actual test case, annotated with @Test. In this case, we are testing the createUser method of UserProfileService. The unit test typically follows the Arrange-Act-Assert (AAA) pattern.
Arrange
We start by creating our required inputs and configuring the behavior of our mocks. We set up a UserCreateRequest, expected UserOrganization, Squad, and other required objects.
For example, we instruct Mockito what to return when the hasUserProfilePrivilege() method of the PrivilegeHandler class is called:
when(privilegeHandler.hasUserProfilePrivilege(Authorities.UserProfile.CREATE)).thenReturn(true);
Similarly, we set up behavior for other mocked objects.
Act
Next, we call the actual method we are testing and store the result:
ResponseEntity<?> actualResponse = userProfileService.createUser(authHeader, userRequest);
Assert
Finally, we assert that the method’s output is as expected. We check if the returned ResponseEntity‘s status code is HttpStatus.OK and if the UserOrganization object returned in the body of the ResponseEntity has the same properties as the expected UserOrganization.
Conclusion
And that’s it! With JUnit and Mockito, you can easily write unit tests to ensure your code behaves as expected under different conditions. You should write a separate @Test method for each condition you wish to test. Remember, each unit test should be independent and test only one condition or functionality.
Next Steps
For more advanced use cases, you might need to use other features of Mockito, like verify() to check that certain methods have been called, ArgumentCaptor to capture method arguments for further assertions, etc. Furthermore, consider using @Nested for better organization of your tests if your test class starts to grow.
@ExtendWith(MockitoExtension.class) public class ApplicationTestingTest { @Mock private AttachmentSourceBean attachmentSourceBean; @Mock private AuthServiceProxy authServiceProxy; @Mock private INTTRABookingRequestRepository inttraBookingRequestRepository; @BeforeEach public void setup() { MockitoAnnotations.initMocks(this); } @Test void testGetCarrierByCarrierInfo(){ String carrierName = "TestCarrier"; String polCountryCode = "US"; ResponseEntity responseEntity = carrierService.getCarrierByCarrierInfo(carrierName,polCountryCode); assertEquals(HttpStatus.OK,responseEntity.getStatusCode()); } @Test void testGetCarrierByCarrierInfoWithException(){ ResponseEntity responseEntity = carrierService.getCarrierByCarrierInfo(null,null); assertEquals(HttpStatus.NOT_FOUND,responseEntity.getStatusCode()); } @Test void getBookingList() throws AccessDeniedException { when(privilegeHandler.hasCarrierBookingPrivilege(any())).thenReturn(true); when(carrierBookingRepository.findAllByIdNotNullOrderByIdDesc()).thenReturn(Collections.EMPTY_LIST); Iterable<CarrierBooking> result = carrierService.getBookingList(); assertEquals(0,result.spliterator().getExactSizeIfKnown()); } @Test void getBookingListWithException() throws AccessDeniedException { assertThrows(AccessDeniedException.class,()->{carrierService.getBookingList();}); } @Test void TestUpDateBooking() throws AccessDeniedException { when(privilegeHandler.hasCarrierBookingPrivilege(any())).thenReturn(true); INTTRABookingRequest inttraBookingRequest = createDemoBookingRequest(); when(inttraBookingRequestRepository.findTop1ByCarrierBooking_ShipmentNumberOrderByIdDesc(any())).thenReturn(inttraBookingRequest); DefaultResponse response = carrierService.updateBooking(inttraBookingRequest); assertEquals("E001",response.getResponseCode()); } @Test void TestUpDateBookingException() throws AccessDeniedException { INTTRABookingRequest inttraBookingRequest = createDemoBookingRequest(); assertThrows(AccessDeniedException.class,()->{carrierService.updateBooking(inttraBookingRequest);}); } @Test void TestUpDateBookingWith() throws AccessDeniedException { when(privilegeHandler.hasCarrierBookingPrivilege(any())).thenReturn(true); assertThrows(NullPointerException.class,()->{carrierService.updateBooking(any());}); }