Unit Test JPA with JUnit H2 In Memory Database

This tutorial shows how to Unit Test JPA with JUnit and a H2 In Memory Database. Some people disagree and don’t call these unit tests, but integration tests. Imho there is some truth in both. The following is not a pure unit test and neither is it a pure integration test. ‘Cause it doesn’t use an identical copy of the production database. That a side, let’s look at the example.

Maven Dependencies

We use Apache Maven to manage the projects dependencies.

<?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
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.memorynotfound.db.hibernate.testing</groupId>
    <artifactId>unit-test-jpa-junit-h2</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>HIBERNATE - ${project.artifactId}</name>
    <url>http://memorynotfound.com</url>

    <properties>
        <mysql.driver.version>6.0.5</mysql.driver.version>
        <hibernate.version>5.2.4.Final</hibernate.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.driver.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

        <!-- testing -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.192</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.0</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.19</version>
            </plugin>
        </plugins>
    </build>

</project>
Make sure the com.h2database:h2 dependency resides on the classpath. This dependency is responsible for creating a pure java in memory database. This results in fast and reliable tests.

Project Structure

Your project structure must look simular to the following.

unit-test-hibernate-jpa-h2-in-memory-database-project-structure

Model Class + JPA Mappings

For simplicity, we’ll work with a single entity. Here is the class, annotated using Java Persistence API Annotations.

package com.memorynotfound.hibernate;

import javax.persistence.*;

@Entity
@NamedQueries(value = {
    @NamedQuery(name = "Book.getAll", query = "SELECT b FROM Book b")
})
public class Book {

    @Id
    private Integer id;
    private String title;

    public Book() {
    }

    public Book(Integer id, String title) {
        this.id = id;
        this.title = title;
    }

    public Integer getId() {
        return id;
    }

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

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", title='" + title + '\'' +
                '}';
    }
}

Configure JPA + In Memory H2 Database

We create a JPA Persistence Unit called mnf-pu-test. This persistence unit is specifically for testing and is located in the src/test/resources/META-INF/persistence.xml file. Note: this file is located under the test folder. This allows us to clearly separate the project with the test environment.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
                                 http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="mnf-pu-test" transaction-type="RESOURCE_LOCAL">

        <!-- add classes -->
        <class>com.memorynotfound.hibernate.Book</class>

        <properties>
            <!-- Configuring JDBC properties -->
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test;INIT=RUNSCRIPT FROM 'classpath:create.sql'\;RUNSCRIPT FROM 'classpath:data.sql'"/>
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>

            <!-- Hibernate properties -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <property name="hibernate.hbm2ddl.auto" value="validate"/>
            <property name="hibernate.format_sql" value="false"/>
            <property name="hibernate.show_sql" value="true"/>

        </properties>
    </persistence-unit>
</persistence>

Since we are using an In-Memory H2 database, we need some slightly different connection properties than a traditional MySQL, MariaDB, Postgrsql or other database vendor. Make sure you use the correct dialect e.g.: org.hibernate.dialect.H2Dialect.

jdbc:h2:mem:<database_name>; creates an in-memory database with a given database name. We can optionally initialize the In Memory H2 Database on application startup. We can pass scripts using the INIT=RUNSCRIPT FROM '<path>' in the connection string.

Database Initialization Scripts

The database scripts are located on the classpath in the src/test/resources folder. The create database tables script is executed on application start.

CREATE TABLE `Book` (
  `id` int(11) NOT NULL,
  `title` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

The data script is executed on application start.

DELETE FROM Book;
INSERT INTO Book(id, title) VALUES (1, 'Unit Test Hibernate/JPA with in memory H2 Database');

Abstract JPA Hibernate Test Case

We wrote a simple class, which manages the EntityManager. This class initializes the EntityManager before the JUnit tests are executed and closes it after the tests are executed. This class also contains a method that resets the database before a method is invoked. This leads to consistent tests across all unit tests. Meaning, tests cannot have an influence on other tests.

package com.memorynotfound.hibernate;

import org.h2.tools.RunScript;
import org.hibernate.Session;
import org.hibernate.jdbc.Work;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.SQLException;

public class JPAHibernateTest {

    protected static EntityManagerFactory emf;
    protected static EntityManager em;

    @BeforeClass
    public static void init() throws FileNotFoundException, SQLException {
        emf = Persistence.createEntityManagerFactory("mnf-pu-test");
        em = emf.createEntityManager();
    }

    @Before
    public void initializeDatabase(){
        Session session = em.unwrap(Session.class);
        session.doWork(new Work() {
            @Override
            public void execute(Connection connection) throws SQLException {
                try {
                    File script = new File(getClass().getResource("/data.sql").getFile());
                    RunScript.execute(connection, new FileReader(script));
                } catch (FileNotFoundException e) {
                    throw new RuntimeException("could not initialize with script");
                }
            }
        });
    }

    @AfterClass
    public static void tearDown(){
        em.clear();
        em.close();
        emf.close();
    }
}

Unit Test JPA with JUnit

We extend from the JPAHibernateTest, that we created earlier. This leads to clean and easy to understand unit tests. We can use the EntityManager to retrieve, insert, update or delete entities from the in-memory database.

package com.memorynotfound.hibernate;

import org.junit.Test;
import java.util.List;
import static junit.framework.TestCase.assertNotNull;
import static org.junit.Assert.assertEquals;

public class JPAHibernateCRUDTest extends JPAHibernateTest {

    @Test
    public void testGetObjectById_success() {
        Book book = em.find(Book.class, 1);
        assertNotNull(book);
    }

    @Test
    public void testGetAll_success() {
        List<Book> books = em.createNamedQuery("Book.getAll", Book.class).getResultList();
        assertEquals(1, books.size());
    }

    @Test
    public void testPersist_success() {
        em.getTransaction().begin();
        em.persist(new Book(10, "Unit Test Hibernate/JPA with in memory H2 Database"));
        em.getTransaction().commit();

        List<Book> books = em.createNamedQuery("Book.getAll", Book.class).getResultList();

        assertNotNull(books);
        assertEquals(2, books.size());
    }

    @Test
    public void testDelete_success(){
        Book book = em.find(Book.class, 1);

        em.getTransaction().begin();
        em.remove(book);
        em.getTransaction().commit();

        List<Book> books = em.createNamedQuery("Book.getAll", Book.class).getResultList();

        assertEquals(0, books.size());
    }

}

References

Download

You may also like...