Controller, Service, and Repository Layer Unit Testing using JUnit and Mockito
By: Md Sajedul Karim
Tags: Junit, Spring Boot, Jpa, Unit Testing, Mockito

3. Practical implementation of each term on controller, service, and repository layer

In a typical project follow the MVC pattern, where there are some controllers, some services, and some repository for data access. In this section, we will write all layers.

Image from www.mitrais.com

Setup the environment

We will work on Gradle based project. We require only three dependencies for cover the testing

We used spring-boot-starter-test for testing framework, mockito and Jupiter. We also used spring-boot-starter-data-jpa for JPA and h2database for the in-memory databases. The project structure is given bellow

From this folder structure, we keep all controllers in the controller package, services in the service package, and repositories in the repository package.

1. Testing repository layer

Here, the repository is based on spring @Repository annotation. Using repository we execute some simple CRUD operations on our database. Our domain class is given below.

In this entity class, we have mainly firstName, lastName, and mobileNumber fields. To access data from this table, our repository class is like bellow

From this repository class, we can execute CRUD operation on Entity Customer

Our test code is as bellow.

From our test case class, we have to know some keywords.

@ExtendWith(MockitoExtension.class): enabling the mockito extension . @DataJpaTest: Annotation that will prepare spring data JPA. It will enable entity-based save, fetch, and other environments. @BeforeEach: Here before execution started, we can initialize some tasks. Here, we are saving a default customer. @AfterEach: AFter execution of all cases, we are doing some tasks here. Here we are removing all our changed data.

Save data on the table

Save a list of entity code is bellow

<S extends Customer> Iterable<S> saveAll(Iterable<S> entities);

Look at the test case

void saveAll_success() {

List<Customer> customers = Arrays.asList(

new Customer("sajedul", "karim", "01737186095"),

new Customer("nafis", "khan", "01737186096"),

new Customer("aayan", "karim", "01737186097")

);

Iterable<Customer> allCustomer = customerRepository.saveAll(customers);



AtomicInteger validIdFound = new AtomicInteger();

allCustomer.forEach(customer -> {

if(customer.getId()>0){

validIdFound.getAndIncrement();

}

});



assertThat(validIdFound.intValue()).isEqualTo(3);

}

Here, we are building 3 customer objects and savings using the customer repository. After saving data we are checking the size of saved data. Here, validity is each entity id must be greater than 0 after savings new data. Here real database transaction is occurring.

Fetch and match data

The repository fetch method is given bellow

@Override

List<Customer> findAll();

And our test code is given bellow

@Test

void findAll_success() {

List<Customer> allCustomer = customerRepository.findAll();

assertThat(allCustomer.size()).isGreaterThanOrEqualTo(1);

}

Here, we are fetching data and checking size 1. Our approximate max customer data size is 1 to 4. Because for default case execute of test cases don’t maintain any order. In the @BeforeEach command, we are inserting only one customer. So we are checking isGreaterThanOrEqualTo(1).

2. Testing Service Layer

Here, the service is based on spring @Service annotation. Using service we check some business logic and save and fetch data to/from the database and return to our controllers.

Notes: We are testing the service layer. So we don't require a database layer actual operation. So we will make the database layer mock.

Our service layer code is given bellow

Here, we have few methods like save or update, fetch data. We will test all of these methods using unit testing.

Our unit testing code is given bellow

In this code, we are mocking the database layer by annotation @Mock CustomerRepository. Here, mocking means this is a dummy layer, no actual operation will happen during database save or fetch. For mocking the database layer we used some stubbing

Save or update

Our save or update test code is given bellow

@Test

public void savedCustomer_Success() {

Customer customer = new Customer();

customer.setFirstName("sajedul");

customer.setLastName("karim");

customer.setMobileNumber("01737186095");



// providing knowledge

when(customerRepository.save(any(Customer.class))).thenReturn(customer);



Customer savedCustomer = customerRepository.save(customer);

assertThat(savedCustomer.getFirstName()).isNotNull();

}

Here, we are building a customer object. Here, the repository save object returns an object after successfully saved into the database. So we are telling mockito that if we try to save any customer object then you return us our provided customer object. Finally, we are checking this returned object. By stubbing we convert database operation with our predefined operation.

Fetch data from the database

Our code for fetching data is given bellow

@Test

public void customer_exists_in_db_success() {

Customer customer = new Customer();

customer.setFirstName("sajedul");

customer.setLastName("karim");

customer.setMobileNumber("01737186095");

List<Customer> customerList = new ArrayList<>();

customerList.add(customer);



// providing knowledge

when(customerRepository.findAll()).thenReturn(customerList);



List<Customer> fetchedCustomers = customerService.fetchAllCustomer();

assertThat(fetchedCustomers.size()).isGreaterThan(0);

}

Here, repository findAll() method return a list of customer. So we are building a list of customers and stubbing the findAll() method and checking the data size after the operation.

3. Testing controller layer

In the controller layer, we are mocking the service layer and testing the API. The controller layer code is given bellow

Here, we have two endpoints one for save and another for fetch data from the service layer.

Our test code is given bellow

Here, we are mocking the CustomerService and for testing API we are using MockMvc. Here, also we are stubbing the service layer methods.

During testing, we are building request and after execution checking the status