How to test Hibernate criteria queries without using any database? - unit-testing

I'm developing a Java application with lots of complex Hibernate criteria queries. I would like to test these criteria to make sure they are selecting the right, and only the right, objects. One approach to this, of course, is to set up an in-memory database (e.g. HSQL) and, in each test, make a round trip to that database using the criteria and then assert that the query results match my expectations.
But I'm looking for a simpler solution, since Hibernate criteria are just a special kind of logical predicates about Java objects. Thus they could, in theory, be tested without accessing any database at all. For example, assuming that there is a entity called Cat:
class Cat {
Cat(String name, Integer age){
this.name = name;
this.age = age;
}
...
}
I would like to do something like this, to create criteria queries:
InMemoryCriteria criteria = InMemoryCriteria.forClass(Cat.class)
.add(Restrictions.like("name", "Fritz%"))
.add(Restrictions.or(
Restrictions.eq("age", new Integer(0)),
Restrictions.isNull("age")))
assertTrue(criteria.apply(new Cat("Foo", 0)))
assertTrue(criteria.apply(new Cat("Fritz Lang", 12)))
assertFalse(criteria.apply(new Cat("Foo", 12)))
The criteria could be used in production code like this:
criteria.getExecutableCriteria(session); //similar to DetachedCriteria
Is there any Java library that makes this kind of test possible?

You could use a mocking framework like Mockito to mock all relevant Hibernate classes and define expected behavior of these mocks.
Sounds like a lot of code, but since the Hibernate Criteria API is a fluent interface, all methods of Criteria return a new instance Criteria. So defining the mock behavior which is common to all tests is simple.
Here is an example using Mockito
#Mock
private SessionFactory sessionFactory;
#Mock
Session session;
#Mock
Criteria criteria;
CatDao serviceUnderTest;
#Before
public void before()
{
reset(sessionFactory, session, criteria);
when(sessionFactory.getCurrentSession()).thenReturn(session);
when(session.createCriteria(Cat.class)).thenReturn(criteria);
when(criteria.setFetchMode(anyString(), (FetchMode) anyObject())).thenReturn(criteria);
when(criteria.setFirstResult(anyInt())).thenReturn(criteria);
when(criteria.setMaxResults(anyInt())).thenReturn(criteria);
when(criteria.createAlias(anyString(), anyString())).thenReturn(criteria);
when(criteria.add((Criterion) anyObject())).thenReturn(criteria);
serviceUnderTest = new CatDao(sessionFactory);
}
All methods of the Criteria mock return the mock again.
In a concrete test you would then use a ArgumentCaptor and verify statements to investigate what happened to the mocked Criteria.
#Test
public void testGetCatByName()
{
ArgumentCaptor<Criterion> captor = ArgumentCaptor.forClass(Criterion.class);
serviceUnderTest.getCatByName("Tom");
// ensure a call to criteria.add and record the argument the method call had
verify(criteria).add(captor.capture());
Criterion criterion = captor.getValue();
Criterion expectation = Restrictions.eq("name", "Tom");
// toString() because two instances seem never two be equal
assertEquals(expectation.toString(), criterion.toString());
}
The problem I see with this kind of unitests is that they impose a lot of expectations about the class under test. If you think of serviceUnderTest
as a blackbox, you can't know how it retrieves the cat object by name. It could also use a LIKE criterion or even 'IN' instead of =, further it could use the
Example criterion. Or it could execute a native SQL query.

I think, you must do an integration test here with H2 or other in-memory database. As you said, if you only use mocks, you can see how object interacts with each other, but you never know what result list you get.
I am on the same page, not with Restriction or so, but with JPA 2.0 CriteriaQuery and CriteriaBuilder. I build complex predicates in my persistence layer, and at last, I find it becomes inevitable to test with data in db, as no one knows what would be the final query in SQL. And I decide that in this part of the system, an integration is needed, so I went for it.
At last it is not very hard to build such a test. You need H2 dependency, a persistence.xml like this:
<?xml version="1.0" encoding="UTF-8"?>
<!-- For H2 database integration tests. -->
<!-- For each int test, define unique name PU in this file and include SQL files in different paths. -->
<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="test-item-history-service-bean" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider> <!-- mind here: must be this! cannot be JPA provider! -->
<class>com.data.company.Company</class>
<class>com.data.company.ItemHistory</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.url"
value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=Oracle;INIT=RUNSCRIPT FROM 'src/test/resources/db/item-history/create.sql'\;RUNSCRIPT FROM 'src/test/resources/db/item-history/populate.sql'"/>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.id.new_generator_mappings" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/> <!-- mind here! Can only be "update"! "create-drop" will prevent data insertion! -->
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.default_schema" value="main"/>
</properties>
</persistence-unit>
</persistence>
(Mind very carefully the comment in the XML above, it took me a week to finally solve them)
Note about the provider: see here: How to configure JPA for testing in Maven
And in the two sql files, you CREATE TABLE ... and INSERT INTO .... Insert whatever you like, as the data is part of the test.
And, a test like this:
/**
* Integration tests with in-memory H2 DB. Created because:
* - In-memory DB are relatively cheap to create and destroy, so these tests are quick
* - When using {#link javax.persistence.criteria.CriteriaQuery}, we inevitably introduce complex perdicates'
* construction into persistence layer, which is a drawback of it, but we cannot trade it with repetitive queries
* per id, which is a performance issue, so we need to find a way to test it
* - JBehave tests are for the user story flows, here we only want to check with the complex queries, certain
* records are returned; performance can be verified in UAM.
*/
#RunWith(MockitoJUnitRunner.class)
public class ItemHistoryPersistenceServiceBeanDBIntegrationTest {
private static EntityManagerFactory factory;
private EntityManager realEntityManager;
private ItemHistoryPersistenceServiceBean serviceBean;
private Query<String> inputQuery;
#BeforeClass
public static void prepare() {
factory = Persistence.createEntityManagerFactory("test-item-history-service-bean");
}
#Before
public void setup() {
realEntityManager = factory.createEntityManager();
EntityManager spy = spy(realEntityManager);
serviceBean = new ItemHistoryPersistenceServiceBean();
try {
// inject the real entity manager, instead of using mocks
Field entityManagerField = serviceBean.getClass().getDeclaredField("entityManager");
entityManagerField.setAccessible(true);
entityManagerField.set(serviceBean, spy);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new AssertionError("should not reach here");
}
inputQuery = new Query<>();
inputQuery.setObjectId("itemId");
}
#After
public void teardown() {
realEntityManager.close();
}
#Test
public void findByIdAndToken_shouldReturnRecordsMatchingOnlyTokenFilter() {
try {
// when
List<ItemHistory> actual = serviceBean.findByIdAndToken(inputQuery);
// then
assertEquals(2, actual.size());
assertThat(actual.get(0).getItemPackageName(), anyOf(is("orgId 3.88"), is("orgId 3.99.3")));
assertThat(actual.get(1).getItemPackageName(), anyOf(is("orgId 3.88"), is("orgId 3.99.3")));
} catch (DataLookupException e) {
throw new AssertionError("should not reach here");
}
}
}

Related

How to test predicate in an unit test?

I have an Apache Camel application, which uses a Choice with a Predicate. How can I test the predicate without an integration test?
Code
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
#Bean
public EndpointRouteBuilder route() {
return new EndpointRouteBuilder() {
#Override
public void configure() throws Exception {
from(file("d:/tmp/camel/"))
.choice()
.when(jsonpath("$[?(#.status == 0)]"))
.log("ok")
.otherwise()
.log("not ok");
}
};
}
}
Research
I read Test JUnit5, but it looks like an integration test. However, I don't want to test a full route.
I read Test Spring JUnit5, but it is an integration test.
Question
How can I extract the predicate jsonpath("$[?(#.status == 0)]") and test it isolated in an unit test with JUnit 5?
Might be hard to accomplish this without a CamelContext. I'd probably approach this with Camel's excellent mocking and stubbing utilities. But if you really just want to isolate the jsonpath expression, you could try something like this:
JsonPathExpression exp = new JsonPathExpression("$[?(#.status == 0)]");
Exchange exchange = new DefaultExchange(context);
final Predicate predicate = exp.createPredicate(context);
exchange.getIn().setBody("{ \"status\": 0 }");
final boolean matches = predicate.matches(exchange);
assertTrue(matches);
Note that you'll still need a CamelContext for this. Typically you'd get it by having the test class extend CamelTestSupport, or if you're in a spring environment, spring can autowire it: #Autowired CamelContext camelContext;
Edit: If you just want to test the JsonPath expression outside of Camel:
String jsonPath = "$[?(#.status == 0)]";
String json = "{ \"status\": 0 }";
DocumentContext jsonContext = JsonPath.parse(json);
JSONArray result = jsonContext.read(jsonPath);
assertEquals(1, result.size());
My opinion (you'll probably get 100 more ;-)
Separate that route into another class by itself that can be loaded into the Spring context later.
Use CamelTestSupport to load just Camel (not Spring) in JUnit.
Use Camel "advice" to change "from" to a direct, or create a file (in your test) to exercise the test case you want (once with each branch of the choice.
Again with "advice" change the log to mocks - then after running the file/message you want check to see if the correct mock got a message and the other did not.

TestNG #Factory and group-by-instances and preserve-order

I'm trying to use for the first time TestNG with #Factory and for me doesn't work, I'll say you why.
I have a class called Extend in which I have some tests, "launch site", "login", "check if the useris in his own dashboard" and so on and I wanted that for all datas passed from the factory the order of theese test are always the same "launch site">>"login">>"check user is in his dashboard">>"logout" ok? So I have the following extend.xml file and classes:
<suite name="ExtendFactory" group-by-instances="true">
<test name="Factory" preserve-order="true" group-by-instances="true">
<classes>
<class name="net.whaooo.ExtendFactory">
<methods>
<include name="launchSite"></include>
<include name="loginTest" />
<include name="userIsInHisOwnDashboardTest" />
<include name="logoutTest" />
</methods>
</class>
</classes>
</test>
</suite>
Extend class:
public class Extend extends BaseTest{
protected static FirefoxDriver driver;
private String a_driver;
private String password;
public Extend(String a_driver, String pwd){
this.a_driver = a_driver;
this.password = pwd;
}
#BeforeTest
public void stDriver() {
DesiredCapabilities caps = DesiredCapabilities.firefox(); caps.setCapability(CapabilityType.ForSeleniumServer.ENSURING_CLEAN_SESSION, true);
driver = new FirefoxDriver(caps);
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#AfterTest
public void stopDriver() {
driver.close();
}
#Test
public void launch() {
launchSite(driver);
}
#Test (description = "Enter a valid login as driver")
public void loginTest() {
login(driver, a_driver, password);
}
#Test (description = "Check the driver is in his own dashboard")
public void userIsInHisOwnDashboardTest(){
userIsInHisOwnDashboardTest(driver, a_driver, password);
}
#Test(description="logout")
public void logout(){
logoutTest(driver);
}
}
Semplified Factory:
public class ExtendFactory {
#Factory
public Object[] createInstances() {
Object[] result = new Object[2];
result[0] = new Extend("test1#test.com","tester");
result[1] = new Extend("test2#test.com","tester");
return result;
}
}
But my problem is that the order in which the tests are launched doesn't follow the one specified in the xml file even if I insert the clause preserve-order="true" group-by-instances="true", I tryed also with order-by-instances="true". Can anyone help me?
I see many issues... first of all #Factory with group-by-instance="true" messes up whole test (it executes just one instance and only non-dependent methods).
#Factory works without group-by-instance but it executes all non-dependent methods first irrespective of number of instances. Eg.. Class A {#Test public void a() {} #Test(dependsOnMethod="a") public void b() {}}... along with #Factory that returns two instances.. then the execution is ref1.a, ref2.a, ref1.b, ref2.b. this has serious issue.. say class A uses large amount of memory then sure it will run out before executing all.
ps: not sure if it is eclipse issue. I am using testng 6.8.1
ps2: seems like testng intends for regression.. but it is still not there.. nor its regression (#Factory) is supported by its own classes (like #Listeners who will read only #Parameters.. but #Factory cannot set same) or by third party.
I think what you need to use is dependsOnMethods in your testcases, coz the flow that you mention, if the first method doesn't execute, there is no point in executing the second testcase. i.e. if "launch site" fails, there's no need to execute "login". This would also ensure order of execution. Check out Dependent Methods
I've been using the
#Test(dependsOnMethods = "TestName")
Where "TestName" is the prerequisite test to run. So for your login test, it should have the following annotation:
#Test(dependsOnMethods = "launchSite")
I'm running 9 tests in a row, and since adding the dependsOnMethods, all have ran in order with no issue
Thank you for your answer, I ended up to use a #Factory specifing "order-by-instances="true"" and than in the dynamic object I insert the dependencies!
Using depends in the TestClass file is not a solution as the functions which are not dependent on any other functions are still been executed randomly.
I need to execute the Test Cases in the order which i have mentioned. This can be achieved using "preserve-order" when executed using TestNG but it fails when grouping is used in TestNG.
If anyone can help in this concern, please revert.

AbstractTransactionalJUnit4SpringContextTests: can't get the dao to find inserted data

I'm trying to set up integration tests using the AbstractTransactionalJUnit4SpringContextTests base class. My goal is really simple: insert some data into the database using the simpleJdbcTemplate, read it back out using a DAO, and roll everything back. JPA->Hibernate is the persistence layer.
For my tests, I've created a version of the database that has no foreign keys. This should speed up testing by reducing the amount of fixture setup for each test; at this stage I'm not interested in testing the DB integrity, just the business logic in my HQL.
/* DAO */
#Transactional
#Repository("gearDao")
public class GearDaoImpl implements GearDao {
#PersistenceContext
private EntityManager entityManager;
/* Properties go here */
public Gear findById(Long id) {
return entityManager.find(Gear.class, id);
}
}
/* Test Page */
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/com/dom/app/dao/DaoTests-context.xml"})
#TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false)
public class GearDaoImplTests extends AbstractTransactionalJUnit4SpringContextTests {
#Autowired
private GearDao gearDao;
#Test
#Rollback(true)
public void quickTest() {
String sql;
// fields renamed to protect the innocent :-)
sql = "INSERT INTO Gear (Gear_Id, fld2, fld3, fld4, fld5, fld6, fld7) " +
" VALUES (?,?,?,?,?,?,?)";
simpleJdbcTemplate.update(sql, 1L, 1L, 1L, "fld4", "fld5", new Date(), "fld7");
assertEquals(1L, simpleJdbcTemplate.queryForLong("select Gear_Id from Gear where Gear_Id = 1"));
System.out.println(gearDao);
Gear gear = gearDao.findById(1L);
assertNotNull("gear is null.", gear); // <== This fails.
}
}
The application (a Spring MVC site) works fine with the DAO's. What could be happening? And where would I begin to look for a solution?
The DAO somehow has a different dataSource than the simpleJdbcTemplate. Not sure how this would be, though, since there's only one dataSource defined in the DaoTests-context.xml file.
Hibernate requires all foreign key relations to be present in order to select out the Gear object. There are a couple of joins that are not present since I'm hardcoding those in fld2/fld3/fld4.
The DAO won't act on the uncommitted data. But why would the simpleJdbcTemplate honor this? I'd assume they both do the same thing.
Underpants gnomes. But where's the profit?
What a difference a couple hours of sleep makes. I woke up and thought "I should check the logs to see what query is actually being executed." And of course it turns out that hibernate was configured to generate some inner joins for a few of the foreign keys. Once I supplied those dependencies it worked like a charm.
I'm loving the automatic rollback on every test concept. Integration tests, here I come!

SQLite unit testing NHibernate generated cascade relationships

The following is some background info on this post. You can just skip to the question if you like:
In this excellent article (http://ayende.com/Blog/archive/2009/04/28/nhibernate-unit-testing.aspx) the author contends that "When using NHibernate we generally want to test only three things:
1) that properties are persisted,
2) that cascade works as expected
3) that queries return the correct result.
-) that mapping is complete & correct (implied)
My take is that he goes on to say that SQLite can and should be the unit test tool of choice to do all of the above. It should be noted that the author seems to be one of more experienced and skilled NHib developers out there, and though he doesn't expressly say so in the article, he implies in a question later that the domain can and should be handling some of SQLite's shortcomings.
QUESTION:
How do you use SQLite to test cascade relationships, especially given that it does not check foreign key constraints. How do you test your model to make sure foreign key constraints will not be a db issue.
Here are some units tests I came up with to test cascade behavior. The model is simply a Department that can have zero to many StaffMembers, with cascade set to NONE.
[Test]
public void CascadeSaveIsNone_NewDepartmentWithFetchedStaff_CanSaveDepartment()
{
_newDept.AddStaff(_fetchedStaff);
Assert.That(_newDept.IsTransient(), Is.True);
_reposDept.SaveOrUpdate(_newDept);
_reposDept.DbContext.CommitChanges();
Assert.That(_newDept.IsTransient(), Is.False);
}
[Test]
public void CascadeSaveIsNone_NewDepartmentWithFetchedStaff_CannotSaveNewStaff()
{
_newDept.AddStaff(_newStaff);
Assert.That(_newDept.IsTransient(), Is.True);
Assert.That(_newStaff.IsTransient(), Is.True);
_reposDept.SaveOrUpdate(_newDept);
_reposDept.DbContext.CommitChanges();
Assert.That(_newDept.IsTransient(), Is.False);
Assert.That(_newStaff.IsTransient(), Is.True);
}
[Test]
public void CascadeDeleteIsNone_FetchedDepartmentWithFetchedStaff_Error()
{
_fetchedDept.AddStaff(_fetchedStaff);
_reposDept.SaveOrUpdate(_fetchedDept);
_reposStaff.DbContext.CommitChanges();
_reposDept.Delete(_fetchedDept);
var ex = Assert.Throws<GenericADOException>(() => _reposDept.DbContext.CommitChanges());
Console.WriteLine(ex.Message);
Assert.That(ex.Message, Text.Contains("could not delete:"));
Console.WriteLine(ex.InnerException.Message);
Assert.That(ex.InnerException.Message, Text.Contains("The DELETE statement conflicted with the REFERENCE constraint"));
}
[Test]
public void Nullable_NewDepartmentWithNoStaff_CanSaveDepartment()
{
Assert.That(_newDept.Staff.Count(), Is.EqualTo(0));
var fetched = _reposDept.SaveOrUpdate(_newDept);
Assert.That(fetched.IsTransient(), Is.EqualTo(false));
Assert.That(fetched.Staff.Count(), Is.EqualTo(0));
}
The third test, ".._FetchedDepartmentWithFetchedStaff_Error" works against Sql Server, but not SQLite since the latter does not check foreign key constraints.
Here are tests for the other side of the relationship; a StaffMember can have one Department, with cascade set to NONE.
[Test]
public void CascadeSaveIsNone_NewStaffWithFetchedDepartment_CanSaveStaff()
{
_newStaff.Department = _fetchedDept;
_reposStaff.SaveOrUpdate(_newStaff);
_reposStaff.DbContext.CommitChanges();
Assert.That(_newStaff.Id, Is.GreaterThan(0));
}
[Test]
public void CascadeSaveIsNone_NewStaffWithNewDepartment_Error()
{
_newStaff.Department = _newDept;
Assert.That(_newStaff.IsTransient(), Is.True);
var ex = Assert.Throws<PropertyValueException>(() => _reposStaff.SaveOrUpdate(_newStaff));
Console.WriteLine(ex.Message);
Assert.That(ex.Message, Text.Contains("not-null property references a null or transient value"));
}
[Test]
public void CascadeDeleteIsNone_FetchedStaffWithFetchedDepartment_DeletesTheStaff_DoesNotDeleteTheDepartment()
{
_newStaff.Department = _fetchedDept;
_reposStaff.SaveOrUpdate(_newStaff);
_reposStaff.DbContext.CommitChanges();
_reposStaff.Delete(_newStaff);
Assert.That(_reposStaff.Get(_newStaff.Id), Is.Null);
Assert.That(_reposDept.Get(_fetchedDept.Id), Is.EqualTo(_fetchedDept));
}
[Test]
public void NotNullable_NewStaffWithUnknownDepartment_Error()
{
var noDept = new Department("no department");
_newStaff.Department = noDept;
var ex = Assert.Throws<PropertyValueException>(() => _reposStaff.SaveOrUpdate(_newStaff));
Console.WriteLine(ex.Message);
Assert.That(ex.Message, Text.Contains("not-null property references a null or transient"));
}
[Test]
public void NotNullable_NewStaffWithNullDepartment_Error()
{
var noDept = new Department("no department");
_newStaff.Department = noDept;
var ex = Assert.Throws<PropertyValueException>(() => _reposStaff.SaveOrUpdate(_newStaff));
Console.WriteLine(ex.Message);
Assert.That(ex.Message, Text.Contains("not-null property references a null or transient"));
}
These tests succed against Sql Server and SQLite. Can I trust the SQLite tests? Are these worthwhile tests?
Cheers,
Berryl
As i understand the article it is about testing the NHibernate Mapping. In my opinion this has nothing to do with db related issues but with testing the nhibernate attributes you set in your mapping. There is no need to assert that it is not possible to create invalid data: you only have to proof that your code creates the desired result and/or checks the things you want to check. You can test cascade, cascade-delete and delete-orphan. whatever you want the way you do it in the tests working with sqlite. But the third test tries to test the constraint, which is nothing nhibernate worries about.
If you want to test your Db contraints you should indeed use your production db and not sqlite. You could do it with or without hibernate but this has nothing to do with your mapping.
If you on the other hand really want a workarround for your Foreign Key tests with SQLite you could try to use this foreign_key_trigger_generator. I haven't tried but it seems to generate before-insert-triggers that assure the existance of the referenced Pk.
Maybe you could write a comment wheather this tool is usefull.

Why are my DBUnit tests consuming so much memory?

I've got a hibernate-based application which uses DBUnit for unit testing. We have an XML test database, which gets loaded up with dummy data in the setUp() of each test and deleted during the tearDown(). The problem is that I can no longer run the entire suite in an IDE (in this case, Intellij), because after about 300 tests, the heap memory gets all used up. The tests go from taking ~0.3 seconds to 30+ seconds to execute, until the JVM eventually gives up and dies.
When I run the test suite via ant's junit task, then it's no problem, nor is running the test suite for an individual class. However, I like being able to run the whole suite locally before I check in large refactoring changes to the codebase rather than breaking the build on the CI server.
I am running the test suite with -Xmx512m as my only argument to the JVM, which is the same amount I pass to ant when running the task on the CI server. My hibernate-test.cfg.xml looks like this:
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:mem:mydatabase</property>
<property name="connection.username">sa</property>
<property name="connection.password"/>
<!-- Other configuration properties -->
<property name="connection.pool_size">1</property>
<property name="jdbc.batch_size">20</property>
<property name="connection.autocommit">true</property>
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<property name="current_session_context_class">thread</property>
<property name="cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
<property name="bytecode.use_reflection_optimizer">false</property>
<property name="show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">create-drop</property>
<!-- Mappings (omitted for brevity) -->
<mapping resource="hbm/blah.hbm.xml"/>
</session-factory>
</hibernate-configuration>
We have written a class for which all of the test classes extend from, which looks something like this:
package com.mycompany.test;
// imports omitted for brevity
public abstract class DBTestCase extends TestCase {
private final String XML_DATA_SET = "test/resources/mytestdata.xml";
private Session _session;
private Configuration _config;
public DBTestCase(String name) {
super(name);
}
#Override
protected void setUp() throws Exception {
super.setUp();
_config = new Configuration().configure();
SessionFactory sf = _config.buildSessionFactory();
// This is a singleton which is used the DAO's to acquire a session.
// The session must be manually set from the test's setup so that any
// calls to the singleton return this session factory, otherwise NPE
// will result, since the session factory is normally built during
// webapp initialization.
HibernateUtil.setSessionFactory(sf);
_session = sf.openSession();
_session.beginTransaction();
IDataSet dataSet = new FlatXmlDataSet(new File(XML_DATA_SET));
DatabaseOperation.CLEAN_INSERT.execute(getConnection(), dataSet);
}
protected void tearDown() throws Exception {
super.tearDown();
_session.close();
}
protected IDatabaseConnection getConnection() throws Exception {
ConnectionProvider connProvider = ConnectionProviderFactory
.newConnectionProvider(_config.getProperties());
Connection jdbcConnection = connProvider.getConnection();
DatabaseConnection dbConnection = new DatabaseConnection(jdbcConnection);
DatabaseConfig dbConfig = dbConnection.getConfig();
dbConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new HsqldbDataTypeFactory());
return dbConnection;
}
}
It is clear that some memory leak is going on here, but I'm not sure where. How might I go about diagnosing this?
You are using memory database here:
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:mem:mydatabase</property>
That means everything in the database is in the memory. Either use on disk database with cached table, or make sure you drop everything after each test.
J-16 SDiZ's answer got me working in the right direction, but I thought I would provide a bit more detailed information as to how I was able to solve this. The root of the problem was indeed that the database kept being stored in memory, but the solution was to inherit from DBUnit's DBTestCase class, not try to roll my own by inheriting from the JUnit TestCase. My test case base class now looks something like this:
public class MyTestCase extends DBTestCase {
private static Configuration _config = null;
public MyTestCase(String name) {
super(name);
if(_config == null) {
_config = new Configuration().configure();
SessionFactory sf = _config.buildSessionFactory();
HibernateUtil.setSessionFactory(sf);
}
System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS, "org.hsqldb.jdbcDriver");
System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL, "jdbc:hsqldb:mem:mydbname");
System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME, "sa");
System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD, "");
}
#Override
protected IDataSet getDataSet() throws Exception {
return new FlatXmlDataSet(new FileReader(MY_XML_DATA_FILE_NAME), false, true, false);
}
#Override
protected void setUpDatabaseConfig(DatabaseConfig config) {
config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new HsqldbDataTypeFactory());
}
This class works quite well, and my test suite runs have gone down from several minutes to a mere 30 seconds.