Best practice in order to integrate liquibase in JUnit – using hibernate JPA with in memory h2 database combined with mockito
In addition to my first liquibase post “basics of continuous database integration with liquibase” I give you some best practice in order to
- integrate liquibase in junit – using hibernate JPA with in memory h2 database
- use mockito to inject the entity manager in a JUnit test
Execute liquibase db.changelog.xml with hibernate JPA and h2 in memory database
Let us start with a simple JUnit test to test our db.changelog.xml file with hibernate JPA entitymanager.
If you don´t have a db.changelog.xml file in your project, just add an example db.changelog.xml.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.0.xsd"> <changeSet author="steffen.mall@exxeta.com" id="example changeset to create a simple table"> <createTable tableName="USER"> <column name="ID" type="LONG"> <constraints nullable="false" /> </column> <column name="NAME" type="VARCHAR(50)"> <constraints nullable="false" /> </column> <column name="EMAIL" type="VARCHAR(50)"> <constraints nullable="true" /> </column> </createTable> <addPrimaryKey tableName="USER" columnNames="ID" /> </changeSet> </databaseChangeLog> |
In our JUnit test we use an in memory h2 database and hibernate JPA. Therefore, just add these dependencies to your pom.xml.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.4.192</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>4.2.14</version> <scope>provided</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> |
Afterwards create your persistence.xml in your test folder (src\test\resources\META-INF). Using h2 in memory database – the connection url looks like jdbc:h2:mem:your-test-db;DB_CLOSE_DELAY=-1;MVCC=true. H2 database store the data In-Memory, this is useful to build and teardown easily a temporary database.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="YOUR_UNIT_NAME"> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" /> <property name="hibernate.connection.url" value="jdbc:h2:mem:your-test-db;DB_CLOSE_DELAY=-1;MVCC=true" /> <property name="hibernate.connection.driver_class" value="org.h2.Driver" /> </properties> </persistence-unit> </persistence> |
Finally create a JUnit test and execute the liquibase file. To get an instance of liquibase you need an instance of liquibase.database.Database and the database needs a java.sql.Connection. For this reason you need to create an instance of the entity manager to get this connetction e.g.:
1 |
EntityManager em = Persistence.createEntityManagerFactory(UNIT_NAME).createEntityManager(); |
After this use the hibernate session to get the connection e.g.:
1 2 |
Connection connection = ((SessionFactoryImpl) em.unwrap(Session.class).getSessionFactory()).getConnectionProvider().getConnection() |
Finally get an instance of liquibase database and liquibase instance to execute the db.changelog.xml. Execute the db.changlog.xml and drop database after the test e.g.:
1 2 3 4 5 |
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection)); Liquibase liquibase = new Liquibase("db.changelog.xml", new ClassLoaderResourceAccessor(), database); liquibase.update("test"); liquibase.dropAll(); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package com.exxeta.blog.liquibase; import java.sql.Connection; import javax.persistence.EntityManager; import javax.persistence.Persistence; import org.hibernate.Session; import org.hibernate.internal.SessionFactoryImpl; import org.junit.Test; import liquibase.Liquibase; import liquibase.database.Database; import liquibase.database.DatabaseFactory; import liquibase.database.jvm.JdbcConnection; import liquibase.resource.ClassLoaderResourceAccessor; public class DBChangelogFileTest { private static final String UNIT_NAME = "YOUR_UNIT_NAME"; @Test public void testDbChangelogFile() throws Exception { // Get entity manager EntityManager em = Persistence.createEntityManagerFactory(UNIT_NAME).createEntityManager(); // Get connection from entitymanager Connection connection = ((SessionFactoryImpl) em.unwrap(Session.class).getSessionFactory()).getConnectionProvider().getConnection(); // Get liquibase database Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection)); // Get liquibase instance / execute update / drop Liquibase liquibase = new Liquibase("db.changelog.xml", new ClassLoaderResourceAccessor(), database); liquibase.update("test"); liquibase.dropAll(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 |
INFORMATION: Successfully acquired change log lock INFORMATION: Creating database history table with name: PUBLIC.DATABASECHANGELOG INFORMATION: Reading from PUBLIC.DATABASECHANGELOG INFORMATION: Reading from PUBLIC.DATABASECHANGELOG INFORMATION: db.changelog.xml: example changeset to create a simple table::steffen.mall@exxeta.com: Table USER created INFORMATION: db.changelog.xml: example changeset to create a simple table::steffen.mall@exxeta.com: Primary key added to USER (ID) INFORMATION: db.changelog.xml: example changeset to create a simple table::steffen.mall@exxeta.com: ChangeSet db.changelog.xml::example changeset to create a simple table::steffen.mall@exxeta.com ran successfully in 9ms INFORMATION: Successfully released change log lock INFORMATION: Successfully acquired change log lock INFORMATION: Dropping Database Objects in schema: your-test-db.PUBLIC INFORMATION: Creating database history table with name: PUBLIC.DATABASECHANGELOG INFORMATION: Successfully released change log lock |
Everything is fine and your db.changelog.xml is tested and ready for other stages. With this simple test you are able to verify the validity of your liquibase file without starting your application server. Additional you can use the trick to setup your database as a base of other test cases.
Additional tip to integrate liquibase using mockito
If you have already integrated JPA tests in your JUnit test landscape – do not use auto ddl in JUnit and liquibase in your live project. You can also use your real db.changelog.xml to setup your test database. In my short example I will use the power of mockito to inject the entitymanager into the example DAO. Setup the database via liquibase db.changelog.xml, use the DAO to execute some database operation or something else. Feel free to implement your abstraction according to your test landscape.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
package com.exxeta.blog.liquibase; import java.sql.Connection; import javax.persistence.EntityManager; import javax.persistence.Persistence; import org.hibernate.Session; import org.hibernate.internal.SessionFactoryImpl; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; import liquibase.Liquibase; import liquibase.database.Database; import liquibase.database.DatabaseFactory; import liquibase.database.jvm.JdbcConnection; import liquibase.resource.ClassLoaderResourceAccessor; @RunWith(MockitoJUnitRunner.class) public class DAOSampleTest { private static final String UNIT_NAME = "YOUR_UNIT_NAME"; @InjectMocks private MyDAO myDao; @Spy private EntityManager em = Persistence.createEntityManagerFactory(UNIT_NAME).createEntityManager(); @Before public void setUpDatabase() throws Exception { // Get connection from entitymanager Connection connection = ((SessionFactoryImpl) em.unwrap(Session.class).getSessionFactory()).getConnectionProvider().getConnection(); // Get liquibase database Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection)); // Get liquibase instance / execute update / drop Liquibase liquibase = new Liquibase("db.changelog.xml", new ClassLoaderResourceAccessor(), database); liquibase.update("test"); liquibase.dropAll(); } @Test public void testSomeEntities() { ... ... myDao.create(new User("name", "name@domain.com")); ... ... } } |
Leave a Comment