Install this theme
Database-driven unit tests with Liquibase

In the previous article “Database change management with Liquibase” i demonstrated the standard Liquibase usage for managing database changes.

This post will describe, how to construct an infstructure for executing database-driven unit tests, a more likely untypical task for Liquibase.

To be able to execute database-driven tests we have to put our database into a known state between test runs. This is where Liquibase will help us.

First, let’s create a sample set of changes that populates the database with the test data:

<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-2.0.xsd">
	<changeSet id="testdata" author="bar">
		<loadData tableName="t_role" file="db/testdata/roles.csv">
			<column name="name" type="STRING" />
			<column name="description" type="STRING" />
		</loadData>
		<loadData tableName="t_user" file="db/testdata/users.csv">
			<column name="id" type="NUMERIC" />
			<column name="username" type="STRING" />
			<column name="role" type="STRING" />
		</loadData>
		<loadData tableName="t_address" file="db/testdata/addresses.csv" />
		<rollback>
			<delete tableName="t_address" />
			<delete tableName="t_user" />
			<delete tableName="t_role" />
		</rollback>
	</changeSet>
</databaseChangeLog> 

In the changelog above we make use of the <loadData> tag that is able to load data from the CSV file and insert it into the database (alternatively you may use <insert>, <update> and <delete> tags to manipulate the database contents). Furthermore the <rollback> block describes how to remove the inserted changes from the database.

As brief overview, here is an example of the roles.csv file:

name,description
USER,A simple user
ADMIN,Administrator user
ANONYMOUS,NULL 

First row in the CSV file specifies the column names to populate. All subsequent rows contains the test data.

Please consult GitHub for other CSV files used in this post: https://github.com/bigpuritz/javaforge-blog/tree/master/liquibase-sample/db/testdata

Now, let’s make our project ready to use Liquibase together with the Junit.

Add following test-scoped dependencies to the pom.xml:

<dependencies>
	<dependency>  <!-- only necessary since we're using H2 as our demonstration database -->
		<groupId>com.h2database</groupId>
		<artifactId>h2</artifactId>
		<version>1.3.168</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.liquibase</groupId>
		<artifactId>liquibase-core</artifactId>
		<version>2.0.5</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.10</version>
		<scope>test</scope>
	</dependency>
</dependencies> 

In the next step we’ll create a skeleton for our testcase, that will put the test data into the database only once on startup and remove it on teardown after all tests are executed.

public class TestWithLiquibase {

	private static Connection conn;
	private static Liquibase liquibase;

	@BeforeClass
	public static void createTestData() throws SQLException,
			ClassNotFoundException, LiquibaseException {

		Class.forName("org.h2.Driver");
		conn = DriverManager.getConnection("jdbc:h2:liquibase-sample", "sa", "sa");

		Database database = DatabaseFactory.getInstance()
				.findCorrectDatabaseImplementation(new JdbcConnection(conn));

		liquibase = new Liquibase("db/testdata/db.testdata.xml",
				new FileSystemResourceAccessor(), database);
		liquibase.update(null);
	}

	@AfterClass
	public static void removeTestData() throws LiquibaseException, SQLException {
		liquibase.rollback(1000, null);
		conn.close();
	}
}	 

Now, writing a database-driven unit tests is quite simple. Following few tests just giving an idea, how to do this:

@Test
public void testUsers() throws SQLException {
	PreparedStatement stmt = null;
	ResultSet rs = null;
	try {
		stmt = conn.prepareStatement("select count(*) as numberOfUsers from t_user");
		rs = stmt.executeQuery();
		rs.next();
		int numberOfUsers = rs.getInt("numberOfUsers");
		Assert.assertEquals(3, numberOfUsers);
	} finally {
		rs.close();
		stmt.close();
	}
}

@Test
public void testRoles() throws SQLException {
	PreparedStatement stmt = null;
	ResultSet rs = null;
	try {
		stmt = conn.prepareStatement("select count(*) as numberOfRoles from t_role");
		rs = stmt.executeQuery();
		rs.next();
		int numberOfUsers = rs.getInt("numberOfRoles");
		Assert.assertEquals(3, numberOfUsers);
	} finally {
		rs.close();
		stmt.close();
	}
}	 

Done.

Consult the GitHub repository for the sample project sources.

 
Blog comments powered by Disqus