Spring Boot Database Integration Test With JUnit and H2

Testing the complete functionality of an application is essential to guarantee that it works according to our expectations. Integration testing helps us and ensures that it works according to our expectations. To test a spring boot application that uses the database, we can leverage built-in support of the spring boot framework that helps to set up the application context. We can also leverage the H2 database, which works as a temporary database, an in-memory database.

In this article, we will learn how to integration test the spring boot application that uses the database. We will use the JUnit support of the spring boot framework and H2 in-memory database.

Table of Contents

Adding the required dependencies

We can use the spring boot’s spring-boot-starter-test starter dependency, which supports JUnit tests and setting up the application context with the provided test configuration.

Notice that we use the PostgreSQL database for main application-related functionality and the H2 in-memory database for integration testing.

The Lombok dependency helps in reducing the boilerplate code.

<?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>
	....
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
...
</project>

A Simple Controller class

We also have a Rest controller called StudentController with a few REST API endpoints shown below.

We will test these endpoints from our integration tests, which internally invoke the service and repository layers and guarantee that the application is working as expected.

@RestController
public class StudentController {

    @Autowired
    private StudentService studentService;

    @GetMapping("/get-students")
    public ResponseEntity<Students> getAllStudents(){
        var list = studentService.getStudents();
        return  new ResponseEntity<>(list, HttpStatus.OK);
    }
    @GetMapping("/get-student")
    public ResponseEntity<Student> getAllStudent(@RequestParam String name){
        return  new ResponseEntity<>(studentService.getStudent(name), HttpStatus.OK);
    }
}

Spring boot database integration test

Now, that our application’s main logic is ready, let’s test the complete functionality by writing the integration tests.

Spring boot provides a spring-boot-starter-test starter dependency that supports configuring the application context with the provided test configurations.

Adding the required configuration

We can add test-related configurations under the src/test/resources directory. Create an application.yml file under the src/test/resources directory.

For test-related configurations, we are going to use the H2 database as shown below.

We have also set the spring.jpa.hibernate.ddl-auto property value to create-drop. Whenever the application starts, this configuration makes sure that it drops the existing database and tables and creates new ones.

spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driverClassName: org.h2.Driver
    username: sa
    password: password
    initialization-mode: always
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    hibernate:
      ddl-auto: create-drop
    show-sql: true
  h2:
    console:
      enabled: true

We will create one test class that tests the complete application functionality by invoking the REST APIs.

Also, we will use Spring boot’s RestTemplate to invoke the APIs and the JdbcTemplate to execute the database-related queries and scripts.

@Slf4j
@TestPropertySource("/application.yml")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SpringBootDatabaseIntegrationTestExampleApplicationTests {

	@LocalServerPort
	private int port;

	@Autowired
	JdbcTemplate jdbcTemplate;

    String baseUrl = "http://localhost:";
	RestTemplate restTemplate;

	@BeforeEach
	void setup() {
		restTemplate = new RestTemplate();
		baseUrl = baseUrl + port;
		log.info("application started with base URL:{} and port:{}", baseUrl, port);
		log.info("Initializing DB..");
		jdbcTemplate.execute("insert into Student(name, grade) values('Jay', 7)");
	}

	@AfterEach
	void emptyData(){
		var totalRecords = jdbcTemplate.queryForObject("Select count(*) from Student", Integer.class);
		log.info("Total Records in DB:{}", totalRecords);
		log.info("Deleting records from table.");
		jdbcTemplate.execute("DELETE FROM Student");
	}
}

Above, we are using the below components to setup our test class.

  • @TestPropertySource: Annotation helps us to configure the test configurations by providing configuration properties. In the above example, we specify the property configuration file that gets loaded from the src/main/test folder.
  • @SpringBootTest: The spring boot provides this annotation that starts the spring boot application wherever a test runs. It also helps to set up the spring application context and uses the provided test configurations.
  • We are also using the SpringBootTest.WebEnvironment.RANDOM_PORT, which starts the spring boot application on a random port number during the test execution.
  • The @LocalServerPort helps us to get the current port on which the spring boot application is running.
  • We are using the setup() method with the @BeforeEach annotation, where we are initializing the application’s base URL, RestTemplate object, and inserting a record on the H2 database using the JdbcTemplate instance.
  • The deleteData() method empties the table after executing each test case.

Also, create a file under the src/test/resources folder with the name initialize-data.sql, and add the below content.

insert into Student(name, grade) values ('Arun', 9);
insert into Student(name, grade) values ('Ajay', 5);

Writing the test cases to validate the functionality

Now that our test configurations are ready, we will write a few test cases that test the application’s functionality.

We will write a few simple test cases that invoke the REST API using the RestTemplate instance.

@Test
@DisplayName("Test Get All available Students")
@Sql(scripts = "classpath:/initialize-data.sql")
void testGetStudents() {
	ResponseEntity<Students> students = restTemplate.getForEntity((baseUrl + "/get-students"), Students.class);
	assertAll(
			() -> assertNotNull(students.getBody()),
			() -> assertNotNull(students.getBody().getStudents()),
			() -> assertEquals(students.getBody().getStudents().size(), 3),
			() -> assertTrue(students.getBody().getStudents().stream()
					.anyMatch(s -> s.getName().equals("Jay")))
	);
}

@DisplayName("Test get student by name")
@Test
void testGetStudentByName() {
	Student student =
			restTemplate.getForObject((baseUrl + "/get-student?name=Jay"), Student.class);
	assertAll(
			() -> assertNotNull(student),
			() -> assertEquals(student.getName(), "Jay")
	);
}
  • @Test: Marks the method as a JUnit test. We are also using the @DisplayName annotation to provide a meaningful name to the test case.
  • We can also leverage the @Sql annotation to specify a method-level SQL script. We can provide inline SQL scripts that are executed just before the test case gets executed.
  • In our example, we have specified a SQL initialization script file name called initialize-data.sql. We can use an external file with SQL scripts we want to execute just before running a test case.

Running the test cases

Let’s run the test cases now.

For every test case, spring boot starts the application using the test configuration provided and executes the tests.

spring boot database integration testing example

Conclusion

In this article, we learned how to write the spring boot database integration test with the help of the H2 database and RestTemplate.

The example code is available on Git hub.

Leave a Reply