# Junit5 Example (with Maven)

In 
Published 2022-12-03

This tutorial explains to you how we can add unit tests in Spring Boot applications (with JUnit5 and Maven). For this example I have used Maven and Java 19.

There are many ways you can get started with a Spring Boot application. One of them is to use Spring initializr.

When this is done we need to add code to that application, which has no functionalities.

Here are the classes you can add in the order to have a functional Spring API service which has one JUnit tests on it:

Employee.java
package com.example.restdemo.employee;

public class Employee {
    private String id;
    private String name;
    private String department;
    private int age;

    public Employee(String id, String name, String department, int age) {
        this.id = id;
        this.name = name;
        this.department = department;
        this.age = age;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", department='" + department + '\'' +
                ", age=" + age +
                '}';
    }
}
EmployeeController.java
package com.example.restdemo.employee;

import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;

@RestController
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    @GetMapping("/employee/all")
    String getAllEmployees() {
        ArrayList<Employee> allEmployees = employeeService.getEmployees();
        String json = new Gson().toJson(allEmployees);
        return json;
    }

    @GetMapping("/employee/age/{empID}")
    String getEmployeeAge(@PathVariable String empID) {
        int empAge = employeeService.getEmployeeAge(empID);
        String empIdJson = "{ \"emp-id\" : \"" + empID + "\", \"age\" : " + empAge + "}";
       // String json = new Gson().toJson(empAge);
        System.out.println(empIdJson);
        return empIdJson;
    }

    @PutMapping(value="/employee/add", consumes = "application/json")
    int addEmployee(@RequestBody Employee newEmployee) {

        employeeService.addEmployee(newEmployee);
        int newCount = employeeService.countEmployees();
        return newCount;
    }

    @DeleteMapping(value="/employee/delete", consumes = "application/json")
    int deleteEmployee(@RequestBody String empId) {

        employeeService.removeEmployee(empId);
        int newCount = employeeService.countEmployees();
        return newCount;
    }
}
EmployeeService.java
package com.example.restdemo.employee;

import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;

@Service
public class EmployeeService {

    private HashMap<String, Employee> employees = new HashMap<>();

    public void addEmployee(Employee Emp) {
       this.employees.put(Emp.getId(), Emp);
    }

    public void removeEmployee(String empId) {
        this.employees.remove(empId);
    }

    public int countEmployees() {
        return this.employees.size();
    }

    public ArrayList<Employee> getEmployees() {

        ArrayList<Employee> empList = new ArrayList<>();
        // go through the values of the map
        for (Employee emp : employees.values()) {
            empList.add(emp);
        }

        return empList;
    }

    //Here we are supposing that we have an external call
    public int getEmployeeAge(String employeeId) {
        return 20;
    }
}

In order to be able to do some unit tests, we need to :

  • add JUnit5 testing framework in pom.xml For doing this we need to remove the old JUnit 4 from the default configuration and add JUnit 5 in pox.xml. For JUnit 5 we need also, to add junit-jupiter-engine as dependency.

take a look at the pom.xml file (used for this application):

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.0.1</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>rest-demo</artifactId>
	<version>v1</version>
	<packaging>jar</packaging>
	<name>rest-demo</name>
	<description>Demo for Spring Boot REST API</description>
	<properties>
		<java.version>19</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>

			<!-- exclude junit 4 -->
			<exclusions>
				<exclusion>
					<groupId>junit</groupId>
					<artifactId>junit</artifactId>
				</exclusion>
			</exclusions>

		</dependency>

		<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
		<dependency>
			<groupId>com.google.code.gson</groupId>
			<artifactId>gson</artifactId>
			<version>2.10.1</version>
		</dependency>

		<!-- junit 5 -->
		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-api</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-engine</artifactId>
			<scope>test</scope>
		</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<version>${parent.version}</version>
			</plugin>
		</plugins>
	</build>

</project>
  • add the unit tests

In may case I have added testEmployeeService() method in the main test class

package com.example.restdemo;

import com.example.restdemo.employee.EmployeeService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertEquals;

@SpringBootTest
class RestDemoApplicationTests {

	@Test
	void contextLoads() {
	}

	@Autowired
	private EmployeeService employeeService;

	@Test
	@DisplayName("Subscription message service test ")
	void testEmployeeService() {

		int message = employeeService.getEmployeeAge("3");
		assertEquals(20, message);
	}

}

If we want to test our application we can go to the project folder and run mvn clean test. We can see something like this:

mvn clean test
[INFO] Scanning for projects...
[WARNING]
[WARNING] Some problems were encountered while building the effective model for com.example:rest-demo:jar:v1
[WARNING] The expression ${parent.version} is deprecated. Please use ${project.parent.version} instead.
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING]
[INFO]
[INFO] -----------------------< com.example:rest-demo >------------------------
[INFO] Building rest-demo v1
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:3.2.0:clean (default-clean) @ rest-demo ---
[INFO] Deleting D:\GitHub-Projects\Projects\Spring-Boot\APIs\SimpleAPI\rest-demo\target
[INFO]
[INFO] --- maven-resources-plugin:3.3.0:resources (default-resources) @ rest-demo ---
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.10.1:compile (default-compile) @ rest-demo ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 4 source files to D:\GitHub-Projects\Projects\Spring-Boot\APIs\SimpleAPI\rest-demo\target\classes
[INFO]
[INFO] --- maven-resources-plugin:3.3.0:testResources (default-testResources) @ rest-demo ---
[INFO] skip non existing resourceDirectory D:\GitHub-Projects\Projects\Spring-Boot\APIs\SimpleAPI\rest-demo\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.10.1:testCompile (default-testCompile) @ rest-demo ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to D:\GitHub-Projects\Projects\Spring-Boot\APIs\SimpleAPI\rest-demo\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ rest-demo ---
[INFO]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.restdemo.RestDemoApplicationTests
20:55:56.840 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [RestDemoApplicationTests]: using SpringBootContextLoader
20:55:56.844 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [com.example.restdemo.RestDemoApplicationTests]: no resource found for suffixes {-context.xml, Context.groovy}.
20:55:56.844 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [com.example.restdemo.RestDemoApplicationTests]: RestDemoApplicationTests does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
20:55:56.865 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using ContextCustomizers for test class [RestDemoApplicationTests]: [ExcludeFilterContextCustomizer, DuplicateJsonObjectContextCustomizer, MockitoContextCustomizer, TestRestTemplateContextCustomizer, DisableObservabilityContextCustomizer, PropertyMappingContextCustomizer, Customizer]
20:55:56.942 [main] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [D:\GitHub-Projects\Projects\Spring-Boot\APIs\SimpleAPI\rest-demo\target\classes\com\example\restdemo\RestDemoApplication.class]
20:55:56.945 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration com.example.restdemo.RestDemoApplication for test class com.example.restdemo.RestDemoApplicationTests
20:55:57.042 [main] DEBUG org.springframework.test.context.util.TestContextSpringFactoriesUtils - Skipping candidate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener] due to a missing dependency. Specify custom TestExecutionListener classes or make the default TestExecutionListener classes and their required dependencies available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]
20:55:57.043 [main] DEBUG org.springframework.test.context.util.TestContextSpringFactoriesUtils - Skipping candidate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener] due to a missing dependency. Specify custom TestExecutionListener classes or make the default TestExecutionListener classes and their required dependencies available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]
20:55:57.045 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using TestExecutionListeners for test class [RestDemoApplicationTests]: [ServletTestExecutionListener, DirtiesContextBeforeModesTestExecutionListener, ApplicationEventsTestExecutionListener, MockitoTestExecutionListener, DependencyInjectionTestExecutionListener, DirtiesContextTestExecutionListener, EventPublishingTestExecutionListener, ResetMocksTestExecutionListener, RestDocsTestExecutionListener, MockRestServiceServerResetTestExecutionListener, MockMvcPrintOnlyOnFailureTestExecutionListener, WebDriverTestExecutionListener, MockWebServiceServerTestExecutionListener]
20:55:57.046 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: class [RestDemoApplicationTests], class annotated with @DirtiesContext [false] with mode [null]

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.0.1)

2023-01-23T20:55:57.333+02:00  INFO 10276 --- [           main] c.e.restdemo.RestDemoApplicationTests    : Starting RestDemoApplicationTests using Java 19.0.1 with PID 10276 (started by Catalin in D:\GitHub-Projects\Projects\Spring-Boot\APIs\SimpleAPI\rest-demo)
2023-01-23T20:55:57.335+02:00  INFO 10276 --- [           main] c.e.restdemo.RestDemoApplicationTests    : No active profile set, falling back to 1 default profile: "default"
2023-01-23T20:55:58.415+02:00  INFO 10276 --- [           main] c.e.restdemo.RestDemoApplicationTests    : Started RestDemoApplicationTests in 1.338 seconds (process running for 2.203)
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.129 s - in com.example.restdemo.RestDemoApplicationTests
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  5.450 s
[INFO] Finished at: 2023-01-23T20:55:59+02:00
[INFO] ------------------------------------------------------------------------

This is a simple Junit test, but in real life the Unit tests are more complicated and imply also using mocks.