In this tutorial, you will learn to implement an integration test of the JPA and Hibernate data layer in Spring Boot by using @DataJPATest annotation with in-memory database

@DataJPATest provides the following features

  • Configure the in-memory test database

  • Auto-scan @Entity classes

  • Auto configure Spring Data JPA, Hibernate and Data Source

  • Turn on SQL logging

Project dependencies

Include spring-boot-starter-test and hsqldb into your pom.xml file

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

<dependency>  
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <scope>test</scope>
</dependency>

spring-boot-starter-test contains some testing support libraries such as JUnit, Spring Test + Spring Boot Test, Mockito, AssertJ, Hamcrest and JsonPath

Define the test class

Run the tests with @RunWith(SpringRunner.class) and @DataJPATest

  • Use @RunWith(SpringRunner.class) class annotation to tell JUnit to run the unit tests in Spring's testing supports

  • Use @DataJPATest class annotation to test JPA and Hibernate layer

Inject dependencies with @Autowired

  • Inject TestEntityManager field
@Autowired
private TestEntityManager testEntityManager;

TestEntityManager is designed for tests, provides an alternative to the JPA standard EntityManager

  • Inject the JPA Repository you want to test, for example
@Autowired
private ProductRespository productRespository;

Verify the test result with AssertJ's assertThat(..).

  • Prefer AssertJ's assertThat to verify the test result for more readability, for example
// when
List<Product> products = productRespository.findAll();

// then
assertThat(products).hasSize(1);

Implementation example

import org.junit.Before;  
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.boot.test.autoconfigure.orm.jpa.TestEntityManager;  
import org.springframework.test.context.junit4.SpringRunner;

import java.math.BigDecimal;  
import java.util.List;

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

@RunWith(SpringRunner.class)
@DataJpaTest
public class ProductRepositoryTest {  
    @Autowired
    private TestEntityManager testEntityManager;

    @Autowired
    private ProductRespository productRespository;

    @Before
    public void setUp(){
        // given
        Product product = Product.builder()
                .name("P1")
                .description("P1 desc")
                .price(new BigDecimal("1"))
                .build();

        testEntityManager.persist(product);
    }

    @Test
    public void whenFindByName_thenReturnProduct() {
        // when
        Product product = productRespository.findByName("P1").get();

        // then
        assertThat(product.getDescription()).isEqualTo("P1 desc");
    }

    @Test
    public void whenFindAll_thenReturnProductList() {
        // when
        List<Product> products = productRespository.findAll();

        // then
        assertThat(products).hasSize(1);
    }
}

@DataJPATest by default will begin a new transaction before and roll back it after running a test method. You should see something like this in the console when running the test

Began transaction (1) for test context [DefaultTestContext@43ee72e6 testClass = ProductRepositoryTest... testMethod = whenFindByName_thenReturnProduct@ProductRepositoryTest...  
...
Rolled back transaction for test:  [DefaultTestContext@43ee72e6 testClass = ProductRepositoryTest... testMethod = whenFindByName_thenReturnProduct@ProductRepositoryTest...

Conclustion

In this article, we learned using @DataJPATest to implement integration test for the data access layer in Spring Boot. You can find the source code on GitHub