@DataR2dbcTest - Testing Spring Data R2DBC components

In this section, we will learn how to test Data R2DBC components with @DataR2dbcTest in the Spring Boot application.

About  @DataR2dbcTest

Instead of bootstrapping the entire application context for every test, @DataR2dbcTest allows us to initialize only the parts of the Application context that are relevant to Data R2DBC components tests.

Regular @Component, @Service or @Controller beans are not scanned when using this annotation. 

This approach not only speeds up the testing process but also ensures a focused and efficient testing environment. 

This approach is also known as "slicing" the application context.

The annotation supports the following attributes:

  • excludeAutoConfigurationAuto-configuration exclusions that should be applied for this test.
  • excludeFilters: beans that would ordinarily be added to the application context can be filtered using a set of exclude filters.
  • includeFilters: beans that would otherwise be filtered and added to the application environment using a set of include filters.
  • properties: Before the test runs, the properties in the key=value format should be added to the Spring Environment.
  • useDefaultFilters: Determines if default filtering should be used with @SpringBootApplication.

Complete Example with @DataR2dbcTest

Next we will create a spring boot Data R2DBC application, create repository layer which contains a query methods and finally we will write test against Real(PostgreSQL) database

Creating a spring boot application

First, open the Spring initializr https://start.spring.io

Then, Provide the Group and Artifact name. We have provided Group name com.knf.dev.demo and Artifact datar2dbctest-annotation-example. Here I selected the Maven project - language Java 17 - Spring Boot 3.2.5Spring Data R2DBC, Lombok, and PostgreSQL Driver.


Final Project Directory


Complete 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.2.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>datar2dbctest-annotation-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>datar2dbctest-annotation-example</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>


application.yaml

We are writing test against real(Postgresql) database. 
So, Let’s configure Spring Boot to use PostgreSQL as our data source. We are simply adding PostgreSQL database URL, username, and password in the src/main/resources/application.yaml
#PostgreSQL configuration
spring:
r2dbc:
url: r2dbc:postgres://localhost:5432/postgres
username: postgres
password: root


Let's create the scripts for testing our application.

Create test-student-schema.sql

DROP table if exists students;
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name VARCHAR(250) NOT NULL,
email VARCHAR(250) NOT NULL,
age INT
);


Create test-student-data.sql

INSERT INTO students (id,name, email, age) VALUES
(101,'Alpha', 'alpha@knf.com', 50),
(102,'Beta', 'beta@knf.com', 40),
(103,'Gama', 'gama@knf.com', 30),
(104,'Pekka', 'pekka@knf.com', 20);


Create Student Entity

Create entity class to represent the students table

package com.knf.dev.demo.entity;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;

@Table(name = "students")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class Student {

@Id
private Long id;
private String name;
private String email;
private Integer age;
}


Create Student Repository

It doesn't make sense to test inherited default methods like save(), findById(), deleteById(), or findAll() from ReactiveCrudRepository. If we are doing like so, means we are testing the framework.

So, i created a method named findStudentByEmail for demonstration.  
package com.knf.dev.demo.repository;

import com.knf.dev.demo.entity.Student;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import reactor.core.publisher.Mono;

public interface StudentRepository
extends ReactiveCrudRepository<Student, Long> {

@Query("SELECT * FROM Student WHERE email = $1")
Mono<Student> findStudentByEmail(String email);

}


Driver class

package com.knf.dev.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Datar2dbctestAnnotationExampleApplication {

public static void main(String[] args) {
SpringApplication.run(Datar2dbctestAnnotationExampleApplication.class, args);
}
}


Implementing the Tests

When using JUnit 4, @SpringBootTest annotation should be used in combination with @RunWith(SpringRunner.class). But for this example we are using JUnit 5, there’s no need to add the equivalent @ExtendWith(SpringExtension.class).

package com.knf.dev.demo;

import com.knf.dev.demo.repository.StudentRepository;
import io.r2dbc.spi.ConnectionFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest;
import org.springframework.core.io.Resource;
import org.springframework.r2dbc.connection.init.ScriptUtils;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import static org.junit.jupiter.api.Assertions.assertEquals;

@DataR2dbcTest
public class StudentRepositoryTests {

@Autowired
private StudentRepository studentRepository;
@Autowired
ConnectionFactory connectionFactory;


@BeforeEach
private void setUp(@Value("classpath:/test-student-data.sql")
Resource script1,
@Value("classpath:/test-student-schema.sql")
Resource script2) {
executeScriptBlocking(script2);
executeScriptBlocking(script1);
}


@Test
void findStudentByEmail_ReturnsTheStudent() {

this.studentRepository.findStudentByEmail("alpha@knf.com")
.as(StepVerifier::create)
.consumeNextWith(p ->
assertEquals("Alpha", p.getName()))
.verifyComplete();
}

private void executeScriptBlocking(final Resource sqlScript) {
Mono.from(connectionFactory.create())
.flatMap(connection -> ScriptUtils
.executeSqlScript(connection, sqlScript))
.block();
}
}
@BeforeEach is used to signal that the annotated method should be executed before each @Test method in the current test class. Here we are creating a table and inserting some dummy data for testing.

Run the test

Or you can run the test using following command:

mvn  test -Dtest=StudentRepositoryTests

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete