Por que meus testes DBUnit consome tanta memória?
-
12-09-2019 - |
Pergunta
Eu tenho um aplicativo baseado em hibernação que usa DBUnit para testes de unidade. Temos um banco de dados de teste XML, que é carregado com dados fictícios na configuração () de cada teste e excluídos durante o tearDown (). O problema é que eu já não pode executar toda a suíte em um IDE (neste caso, Intellij), porque depois de cerca de 300 testes, a memória heap fica tudo esgotado. Os testes vão de tomar ~ 0,3 segundos a mais de 30 segundos para executar, até que a JVM finalmente desiste e morre.
Quando eu executar o teste via tarefa junit formiga, então não é nenhum problema, nem está a executar o conjunto de testes para uma classe individual. No entanto, eu gosto de ser capaz de executar toda a suíte localmente antes de check-in grandes alterações de refatoração para a base de código em vez de quebrar a compilação no servidor de CI.
Estou executando o conjunto de testes com -Xmx512m como meu único argumento para a JVM, que é a mesma quantidade que eu passar a formiga quando executar a tarefa no servidor de CI. Meus hibernate-test.cfg.xml esta aparência:
<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>
Nós escrevemos uma classe para a qual todas as classes de teste se estendem desde, que é algo como isto:
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;
}
}
É claro que alguns vazamento de memória está acontecendo aqui, mas eu não tenho certeza de onde. Como eu poderia ir sobre diagnosticar isso?
Solução 2
J-16 do SDiZ me trabalhar na direção certa, mas eu pensei que eu iria fornecer informações um pouco mais detalhada de como eu era capaz de resolver isso. A raiz do problema era realmente que o banco de dados mantido sendo armazenados na memória, mas a solução era herdar de classe DBTestCase de DBUnit, não tentar fazer a minha própria por herança da JUnit TestCase. Meu teste classe base caso agora é algo como isto:
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());
}
Esta classe funciona muito bem, e minha suíte teste é executado ter ido para baixo de vários minutos para apenas 30 segundos.
Outras dicas
Você está usando o banco de dados de memória aqui:
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:mem:mydatabase</property>
Isso significa que tudo no banco de dados está na memória. De qualquer uso em banco de dados em disco com a tabela em cache, ou certifique-se de largar tudo depois de cada teste.