🛠️ Mastering Unit Testing with Mockito and JUnit5! 🚀

🛠️ 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]

Happy coding!

Developed by Piko Dev => Hamza Braimi :)