When writing unit tests in Java there might be the need to access a resource in the class that should be tested. A common way is to mock the resource. But mocking a simple resource is quite easy. This is supported by mocking frameworks like Mockito, EasyMock or PowerMock. It is a good idea in case you want to test a single action on the mocked resource – e.g. returning a value when accessing a map that contains master data. The following exmaple describes mocking a resource using Mockito. But in case there are complex operations on this resource mocking it is going to be really nasty.
Map<String,String> mock = mock(Map.class); when(mock.get("key")).thenReturn("value");
Mocking the file system
When writing a component that writes to the file system, in this case the interaction with file system must be mocked in order to beeing able to test the code properly. But mocking this requires to correctly mock it by 100% to get reliable results. Fortunately there is a JUnit feature called rule. Exaclty for this scenario JUnit provides the rule TemporaryFolder. Using this rule, JUnit provides a temporary folder before executing the test. All the interaction with the file system during test is written in this directory. After the test this temporary folder is deleted correctly by JUnit. This means there is no interaction with any directories the build process is affected by. There is no file that survives the end of a test and no resource the build process relies on, is deleted, modified or created.
@Rule public TemporaryFolder folder = new TemporaryFolder(); @Test public void test() throw IOException { new ClassUnderTest().createNewFile(folder); assertEquals(1,file.list().length); }
Mocking a EntityManager
A complex resource might be in a Java EE scenario the entity manager. The entity manager provides full database access. Mocking this functionality is possible, but this requires deep knowledge of the JPA standard to correctly set up the mock. Furthermore it might be interesting to know whether the interaction with the object relational mapper works as expected. So providing the resource not by mocking, but by providing a real implementation is a definitly a way of choice. This works as using a JPA provider in a Java SE context is possible. A container is not required to use JPA. In case of the jpa provider each implementation works a little bit different, so testing against a number of implementations might result in different testing results. Features that are not clearly written down in the JPA specification may be handled differently by each provider.The following describes the usage of an entity manager in a unit test scenario.
Preparing Persistence
When accessing the entity manager, the database must be set up before the test. After the test a cleanup has to remove all the modifications written to the database during testing. So the rule must provide a fully working database before the first testcase is called. Futhermore the rule must ensure a clean database after the testcase that does not affect the other testcases.
Setting up the database
When creating the entity manager, all the tables, indexes and constraints can be inserted in to database automatically. To force the persistence provider to do so, this must be configured in the persistence.xml. In the persistence.xml the database driver must be added, furthermore the database driver must be in the classpath. In this post hsqldb is used.
Rolling back pending changes to database
The rule cannot know all the changes written do database. But the entity manager can. Can as it knows the state only if the database is accessed transactionally. But as the entity manager is doing this work, the transactions simply must be rolled back after the test. So the database can be accessed by tests without interfering it. Alternatively the database may be set up for each test. But this is quite slow and it is not possible to run tests concurrently.
The JUnit-Rule
@Rule public ExternalResource resource = new ExternalResource() { EntityManagerFactory emf = Persistence.createEntityManagerFactory("testUnit"); EntityManager em; EntityTransaction tx; @Override protected void before() throws Throwable { em = emf.createEntityManager(); tx = getTransaction(); tx.begin(); } @Override protected void after() { try { flush(); } finally { try { if (tx != null) tx.rollback(); } finally { close(); } } } public EntityManager getEntityManager() { return em; } };
A new transaction is created before the test. After the test the transaction is rolled back to revert all the pending changes to the database. Before rolling back em.flush()
forces to entity manager to write the changes to the database. Constraint violations are checked now. In case there is a violation an PersistenceException is thrown. A correct setup for the entity manager is required. So putting a working persistence.xml into classpath is required.
<?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="testUnit" transaction-type="RESOURCE_LOCAL"> <properties> <property name="eclipselink.ddl-generation" value="create-tables" /> <property name="eclipselink.ddl-generation.output-mode" value="database" /> <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:mem:." /> <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver" /> <property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.HSQLPlatform" /> <!-- further configuration --> </properties> <!-- further configuration --> </persistence-unit> </persistence>
For testing purpose eclipselink is used as persistence provider. Eclipselink is the reference implementation for JPA 2.0.
eclipselink.ddl-generation
Generates all the statements for the specified database.
eclipselink.ddl-generation.output-mode
Put statements into database on startup.
javax.persistence.jdbc.driver
Setting database driver.
eclipselink.target-database
Telling eclipselink witch syntax to use.
Final thoughts
Mocking resources in a unit test is quite simple. Spending time in mocking resources as soon as possible gives a feedback whether the code is working or not. Even in a Java EE scenario it is possible to mock e.g. the entity manager. It also would be possible to provide CDI this way. Thus is is not required to wait until the whole container is running to test the code, but running this tests out of the ide is possible. In each case there is a resource that cannot provided during test, this way of mocking simplifies testing tremendously.
Sources:
Documentation about JUnit Rules
EasyMock
PowerMock
Mockito
JPA specification
Eclipselink
JUnit