Most interviewers like Java candidates those who are passionate and experienced about writing unit tests. Any non trivial Java application will be making calls to database tables. So, here are a few questions and answers testing your ability to write unit tests to test data access layer.
Q1. How will you go about unit testing the data access logic?
A1. The main challenge with using a ‘real’ database” for unit testing is the setup, take down, and isolation of the tests. You don’t want to have to spin up an entirely new Oracle database and create tables and data just for unit testing.
Mocking the database is one option when testing the service layer, however for testing the DAO itself you need something behind the DAO has the data and the queries to run properly.
Solution: The solution is to use an in memory database. HyperSQL (i.e. HSQLDB) is an excellent choice for this because it has the ability to emulate the dialect of another database.
Q2. Can you describe the basic steps involved in setting up an in memory database in Java apps?
A2.
Step 1: The pom.xml file that brings in the libraries required for testing — the hsqldb and spring-test jars.
1 2 3 4 5 6 7 8 9 10 11 |
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version> 3.2.13.RELEASE</version> </dependency> <dependency> <groupId> org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.3.2</version> </dependency> |
Step 2: The Spring configuration file to bootstrap HSQLDB. One of the new features introduced in Spring 3 is the support for embedded Java database engines. Embedded databases like HSQL, H2, or Derby are very useful during the development phase of the project as they are fast, have small memory footprints and are opensource. “EmbeddedDatabaseBuilder” will make things easier.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package com.jpa.tutorial.config; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; @Configuration @Import(MyDomainConfiguration.class) public class MyAppCommonsDomainTestConfiguration { @Bean public DataSource dataSource() { final EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); builder.setType(EmbeddedDatabaseType.HSQL); builder.addScript("classpath:hsqldb-oracle.sql"); builder.addDefaultScripts(); // defaults are schema.sql and data.sql return builder.build(); } } |
Step 3: The default schema.sql and data.sql along with the hsqldb-oracle.sql need to be in the classpath. Say in src/test/resources
hsqldb-oracle.sql
1 2 |
SET DATABASE SQL SYNTAX ORA TRUE |
schema.sql
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
------------------------------ -- Drop the current schema ------------------------------ DROP TABLE IF EXISTS account; ------------------------------ -- Create new schema ------------------------------ CREATE TABLE account( account_id NUMERIC(10,0) NOT NULL, account_code VARCHAR(32) NOT NULL, CONSTRAINT account_pk PRIMARY KEY (account_id) ); |
data.sql dummy data for testing
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
------------------------------ -- Truncate Tables ------------------------------ TRUNCATE TABLE account; ------------------------------ -- Populate Schema ------------------------------ INSERT INTO account (account_id, account_code) VALUES (1, '456'); COMMIT; |
The above steps would have created the schema and data for the unit testing purpose in memory.
It is also flexible enough to specifically configure the scripts like
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Value("classpath:com/myapp/sql/db-schema.sql") private Resource schemaScript; @Value("classpath:com/myapp/sql/db-test-data.sql") private Resource dataScript; @Bean public DataSourceInitializer dataSourceInitializer(final DataSource dataSource) { final DataSourceInitializer initializer = new DataSourceInitializer(); initializer.setDataSource(dataSource); initializer.setDatabasePopulator(databasePopulator()); return initializer; } private DatabasePopulator databasePopulator() { final ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); populator.addScript(schemaScript); populator.addScript(dataScript); return populator; } |
if you are doing it via XML based spring config, it will be something like
1 2 3 4 5 |
<jdbc:script location="classpath:schema.sql"/> <jdbc:script location="classpath:data.sql"/> </jdbc:embedded-database> |
The above approach can help you identify and fix any object-to-relational mapping issues, spring-jpa-hibernate wiring issues, and other coding issues.
Q3. How the heck to unit test business logic with a database hanging around?
A3. One of the best ways to get the database out of your way is to hide data access behind abstracted interfaces that can be mocked in business logic testing.
There are a few libraries that help you mock database logic.
1) MockRunner: has some JDBC-specific extensions that allow for simulating JDBC ResultSets, as well as for checking whether actual queries are executed.
2) An ordinary Java mocking libraries like jMock, EasyMock, Mockito etc mock the DAO layer. This approach was explained in Q2. Mocking DAO Layer
Q4. What are some of the things you don’t have to unit test?
A4. You should never write unit test for the sake of writing one. There are scenarios where you don’t have to write unit tests. For example,
1) Third-party frameworks and libraries. You should assume that they work as expected.
2) Code that gives non deterministic results.
3) Very complex database logic invoking stored procedures, etc. It is much easier to test basic CRUD operations.
4) Trivial code like getters and setters.