Spring Boot @DataJpaTest tutorial

Spring Boot @DataJpaTest tutorial shows how to test JPA repositories using @DataJpaTest annotation.

Spring is a popular Java application framework for creating enterprise applications. Spring Boot is an evolution of Spring framework which helps create stand-alone, production-grade Spring based applications with minimal effort.


@DataJpaTest is used to test JPA repositories. It is used in combination with @RunWith(SpringRunner.class). The annotation disables full auto-configuration and applies only configuration relevant to JPA tests. By default, tests annotated with @DataJpaTest use an embedded in-memory database.

In our tests, we can inject a DataSource, @JdbcTemplate, @EntityManager or any Spring Data repository from our application.

The application context containing all these components, including the in-memory database, is shared between all test methods within all test classes annotated with @DataJpaTest. Therefore, each test method runs in its own transaction, which is rolled back after the method has executed. This way the tests stay independent from each other.

Spring @DataJpaTest example

The following application creates a custom JPA query method. The method is tested in a test class annotated with @DataJpaTest.

│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           │   MyRunner.java
│   │           ├───model
│   │           │       City.java
│   │           └───repository
│   │                   CityRepository.java
│   └───resources
│           application.properties
│           data-h2.sql
│           schema-h2.sql

This is the project structure.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"













The Maven POM file contains dependencies for Spring Data JPA, testing, and H2 database.


The application.properties is the main Spring Boot configuration file. With the spring.main.banner-mode property we turn off the Spring banner. The spring.datasource.platform sets the vendor name of the database. It is used in the initialization scripts. Finally, the spring.jpa.hibernate.ddl-auto disables the automatic creation of schemas from entities.

    name VARCHAR(255), population INT);

When the application is started, the schema-h2.sql script is executed. It creates a new database table.

INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Suzhou', 4327066);
INSERT INTO cities(name, population) VALUES('Zhengzhou', 4122087);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
INSERT INTO cities(name, population) VALUES('Brest', 139163);
INSERT INTO cities(name, population) VALUES('Bucharest', 1836000);

Later, the data-h2.sql file is executed. It fills the table with data.

package com.zetcode.model;

import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Table(name = "cities")
public class City {

    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;
    private int population;

    public City() {

    public City(String name, int population) {
        this.name = name;
        this.population = population;

    public Long getId() {
        return id;

    public void setId(Long id) {
        this.id = id;

    public String getName() {
        return name;

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

    public int getPopulation() {
        return population;

    public void setPopulation(int population) {
        this.population = population;

    public int hashCode() {
        int hash = 7;
        hash = 79 * hash + Objects.hashCode(this.id);
        hash = 79 * hash + Objects.hashCode(this.name);
        hash = 79 * hash + this.population;
        return hash;

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        if (obj == null) {
            return false;
        if (getClass() != obj.getClass()) {
            return false;
        final City other = (City) obj;
        if (this.population != other.population) {
            return false;
        if (!Objects.equals(this.name, other.name)) {
            return false;
        return Objects.equals(this.id, other.id);

    public String toString() {

        var builder = new StringBuilder();
        builder.append("City{id=").append(id).append(", name=")
                .append(name).append(", population=")

        return builder.toString();

This is the City entity.

package com.zetcode.repository;

import com.zetcode.model.City;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

public interface CityRepository extends CrudRepository<City, Long> {

    @Query("SELECT c FROM City c WHERE c.name LIKE CONCAT('%',:ending, '%') AND c.population < :num")
    List<City> findByNameEndingWithAndPopulationLessThan(@Param("ending") String ending,
                                                         @Param("num") Integer num);

CityRepository contains the custom findByNameEndingWithAndPopulationLessThan() method. With the method we get all city names that end with the specified string and their population is lower than the specified value.

package com.zetcode;

import com.zetcode.repository.CityRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

public class MyRunner implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);

    private CityRepository cityRepository;

    public void run(String... args) throws Exception {

        var cities = cityRepository.findByNameEndingWithAndPopulationLessThan("est", 1800000);
        cities.forEach(city -> logger.info("{}", city));

In MyRunner we use the findByNameEndingWithAndPopulationLessThan() method.

Note: In Java enterprise applications it is a good practice to define a service layer that works with repositories. For simplicity reasons, we skip the service layer.

package com.zetcode;

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

public class Application {

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

The Application sets up the Spring Boot application. The @SpringBootApplication enables auto-configuration and component scanning.

package com.zetcode.repository;

import com.zetcode.model.City;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.junit4.SpringRunner;

import static org.assertj.core.api.Assertions.assertThat;

public class CityRepositoryTest {

    private CityRepository repository;

    public void should_find_all_customers() {

        Iterable<City> cities = repository.findAll();

        int nOfCities = 12;

    public void should_find_with_name_ending_population_less_than() {

        var cities = repository.findByNameEndingWithAndPopulationLessThan("est", 150000);



In CityRepositoryTest, we test the custom JPA method.

public class CityRepositoryTest {

The CityRepositoryTest is annotated with @DataJpaTest. The in-memory H2 database is used to perform the integration tests.

public void should_find_with_name_ending_population_less_than() {

    var cities = repository.findByNameEndingWithAndPopulationLessThan("est", 150000);


This method tests that there is at least one city with name ending in 'est' and with population less than 150000.

$ mvn spring-boot:test

We run the tests.

In this tutorial, we have showed how to test a custom JPA repository method utilizing @DataJpaTest.

List all Spring Boot tutorials.