🛠️ Mastering Unit Testing with Mockito and JUnit5! 🚀
When you're creating a Java application, ensuring it works as expected is crucial. This is where JUnit testing comes in. JUnit is a unit testing framework for the Java programming language, playing a crucial role in test-driven development (TDD) to ensure your code works correctly from the beginning. Although not built into the Java language, JUnit is widely used by Java developers for unit testing.
Step 1: Installing JUnit
To install JUnit, add the following dependency to your project:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<!-- version -->
</dependency>
Basic Structure of a JUnit Test
Once installed, you can start writing tests. Each test method is annotated with @Test
and contains the code to test a particular unit of functionality. Here’s a simple example:
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class MyFirstJUnitTest {
@Test
public void testAddition() {
int result = 1 + 1;
assertEquals(2, result);
}
}
Making Assertions in JUnit
Assertions are core components of tests, verifying that your code is working as expected. JUnit provides various assertion methods like assertEquals
, assertTrue
, assertThat
, assertNull
, and more.
@Test
public void testFindCustomerByEmail(){
String email = "hamza@gmail.com";
Optional<Customer> result = customerRepository.findByEmail(email);
AssertionsForClassTypes.assertThat(result).isPresent();
}
Unit Testing in Microservices
To begin unit testing in microservices, start by creating a customer service microservice and install the required dependencies.
Customer Entities
@Entity
@AllArgsConstructor @NoArgsConstructor @Getter @Setter @Builder @ToString
public class Customer {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id ;
@NotEmpty
@Size(min = 3)
private String firstName;
@NotEmpty @Size(min = 3)
private String lastName;
@NotEmpty @Size(min = 5)
@Column(unique=true)
private String email;
}
Customer Repository
public interface CustomerRepository extends JpaRepository<Customer,Long> {
Optional<Customer> findByEmail(String email);
List<Customer> findByFirstNameContainsIgnoreCase(String keyword);
}
Customer DTO
@AllArgsConstructor @NoArgsConstructor @Getter @Setter @Builder @ToString
public class CustomerDTO {
private Long id ;
@NotEmpty
@Size(min = 3)
private String firstName;
@NotEmpty @Size(min = 3)
private String lastName;
@NotEmpty @Size(min = 5)
@Column(unique=true)
private String email;
}
Customer Mapper
@Service
public class CustomerMapper {
@Service
public class CustomerMapper {
private ModelMapper modelMapper = new ModelMapper();
public CustomerDTO fromCustomer(Customer customer){
return modelMapper.map(customer,CustomerDTO.class);}
public Customer fromCustomerDTO(CustomerDTO customerDTO){
return modelMapper.map(customerDTO,Customer.class);}
public List<CustomerDTO> fromListCustomer(List<Customer> customerList){
return customerList.stream().map(cust->modelMapper.map(cust, CustomerDTO.class)).collect(Collectors.toList());
}
}
Customer Repository Test
Test all functions defined in the Customer Repository interface.
@ActiveProfiles("test")
@DataJpaTest
class CustomerRepositoryTest {
@Autowired
private CustomerRepository customerRepository;
@BeforeEach
public void setUp(){
customerRepository.save(Customer.builder()
.firstName("HAMZA").lastName("BRAIMI").email("hamza@gmail.com").build());
customerRepository.save(Customer.builder()
.firstName("Ahmed").lastName("Yassine").email("ahmed@gmail.com").build());
customerRepository.save(Customer.builder()
.firstName("Hanane").lastName("yamal").email("hanane@gmail.com").build());
}
@Test
public void testFindCustomerByEmail(){
String email="hamza@gmail.com";
Optional<Customer> result=customerRepository.findByEmail(email);
AssertionsForClassTypes.assertThat(result).isPresent();
}
@Test
public void testNotFindCustomerByEmail(){
String email="hamzacdcd@gmail.com";
Optional<Customer> result=customerRepository.findByEmail(email);
AssertionsForClassTypes.assertThat(result).isEmpty();
}
@Test
public void testFindCustomersByFirstName(){
String name="a";
List<Customer> expectedList=List.of(
Customer.builder()
.firstName("HAMZA").lastName("BRAIMI").email("hamza@gmail.com").build(),
Customer.builder()
.firstName("Ahmed").lastName("Yassine").email("ahmed@gmail.com").build(),
Customer.builder()
.firstName("Hanane").lastName("yamal").email("hanane@gmail.com").build()
);
List<Customer> customerList=customerRepository.findByFirstNameContainsIgnoreCase(name);
assertThat(customerList).isNotNull();
assertThat(customerList.size()).isEqualTo(expectedList.size());
// Compare By Content
assertThat(customerList).usingRecursiveComparison().ignoringFields("id").isEqualTo(expectedList);
}
}
Mockito - JUnit Integration
After starting this part, we should understand what Mockito is and why we need it ?
\=> Mockito is a mocking framework, JAVA-based library that is used for effective unit testing of JAVA applications. Mockito is particularly valuable in until testing because it helps verify interactions between different parts of the code and ensures that each unit behaves as expected. Mockito is used to mock interfaces so that a dummy functionality can be added to a mock interface that can be used in unit testing. This tutorial should help you learn how to create unit tests with Mockito as well as how to use its APIs in a simple and intuitive way.
\=> @ExtendWith(MockitoExtension.class): This annotation is used to enable Mockito support for this test class, allowing the usage of Mockito annotations like Bella Mock @InjectMocks, etc.
\=> @Mock: This annotation is used to mock dependencies or collaborators of the class under test (CustomerServiceImp). In this case, CustomerRepository and CustomerMapper are being mocked.
\=> @InjectMocks: This annotation injects mock dependencies into the class under test (CustomerServiceImp).
@Test: This annotation denotes that the method annotated with it is a test method.
@ExtendWith(MockitoExtension.class)
class CustomerServiceImpTest {
@Mock
private CustomerRepository customerRepository;
@Mock
private CustomerMapper customerMapper;
@InjectMocks
private CustomerServiceImp undertestCustomerServiceImp;
@Test
public void testSaveNewCustomer(){
// Create a CustomerDTO object with sample data
CustomerDTO customerDTO = CustomerDTO.builder()
.firstName("HAMZA").lastName("BRAIMI").email("hamza@gmail.com").build();
// Create a sample saved customer object
Customer savedCustomer = Customer.builder()
.firstName("HAMZA").lastName("BRAIMI").email("hamza@gmail.com").build();
// Create another customer object with the same data
Customer customer = Customer.builder()
.firstName("HAMZA").lastName("BRAIMI").email("hamza@gmail.com").build();
// Create an expected CustomerDTO object with the same data
CustomerDTO expected = CustomerDTO.builder()
.firstName("HAMZA").lastName("BRAIMI").email("hamza@gmail.com").build();
// Mocking the behavior of customerRepository to return an empty optional when findByEmail is called
Mockito.when(customerRepository.findByEmail(customerDTO.getEmail())).thenReturn(Optional.empty());
// Mocking the behavior of customerMapper to return customer when fromCustomerDTO is called with customerDTO
Mockito.when(customerMapper.fromCustomerDTO(customerDTO)).thenReturn(customer);
// Mocking the behavior of customerRepository to return savedCustomer when save is called with customer
Mockito.when(customerRepository.save(customer)).thenReturn(savedCustomer);
// Mocking the behavior of customerMapper to return expected when fromCustomer is called with savedCustomer
Mockito.when(customerMapper.fromCustomer(savedCustomer)).thenReturn(expected);
// Calling the method to be tested
CustomerDTO result = undertestCustomerServiceImp.saveNewCustomer(customerDTO);
// Assertions to ensure that result is not null and is equal to expected
AssertionsForClassTypes.assertThat(result).isNotNull();
AssertionsForClassTypes.assertThat(expected).usingRecursiveComparison().isEqualTo(result);}
Finally, as usual, you can find the entire project on my GitHub account by clicking Visit Project, complete with a very detailed README. Don't hesitate to reach out for anything at all. [GitHub link: https://github.com/HAMZA12337/Spring-Boot---Micro-Services---JUnit5--Mockito]
Developed by Piko Dev => Hamza Braimi :)
Subscribe to my newsletter
Read articles from HAMZA BRAIMI directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by