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>https://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.
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">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 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());
}
}
Hi Memorynotfound. The persistence.xml requires a provider. which should be used?
Hello Rajiv,
It’s not required, but you can use the following
HibernatePersistenceProvider</code.
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
Kr,
Memorynotfound
Hi Memorynotfound!
I have a problem with the code, when I use it in my project and try to run a mvn clean package I get a NoClassDefFound error. I have followed your tutorial to the point, so I’m woundering if you got any pointers?
/Tore
Dear Tore,
The error indicates that you’re missing an external library.
Have you included the exact third party libraries defined in the pom.xml?
Can you share the exact error message you’re receiving?
Kr,
Memorynotfound
I only get this sunfire report:
java.lang.NoClassDefFoundError: javax/transaction/SystemException
Caused by: java.lang.ClassNotFoundException: javax.transaction.SystemException
The weird part is that the test works in Intellij but not when I try to mvn clean package
/Tore
I found the problem!
I were using Java9 and it was doing some strang things, so it works when I set JAVA_HOME to java1.8