Create Middleware Integration Test with DB

Hi,

i want to create a middleware integration test according to the docs.

When creating a database with the createTestDb Task:




    task createTestDb(dependsOn: assembleDbScripts, description: 'Creates local hsqldb database for tests', type: CubaDbCreation) {
        dbms = 'hsql'
        host = 'localhost'
        dbName = 'testDb'
        dbUser = 'sa'
        dbPassword = ''
    }

and running it from the command line “./gradlew createTestDb” i get a


localhost:cubaTest mario$ ./gradlew createTestDb
:app-core:assembleDbScripts UP-TO-DATE
:app-core:createTestDb
Using database URL: jdbc:hsqldb:hsql://localhost/testDb, user: sa
Executing SQL: drop schema public cascade;
java.sql.SQLTransientConnectionException: java.net.ConnectException: Connection refused
Using database URL: jdbc:hsqldb:hsql://localhost/testDb, user: sa
:app-core:createTestDb FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app-core:createTestDb'.
> java.sql.SQLTransientConnectionException: java.net.ConnectException: Connection refused

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 9.118 secs

Which leads to the situation that i can’t run the Unit Tests, because it is referring to a table that does not exists (obviously). Im not sure what i have to change to get a running test db.

Another question would be, how is it possible that i get a fresh copy of in memory db, that will be created / deleted after each test?

Any help would be appreciated - thx.

Bye,
Mario

Hi Mario,

When you work with HSQL database through a server connection (not in-process, which is defined by the connection URL), you need an HSQL server running. You may not notice this requirement because CUBA Studio runs HSQL server when you open a project that uses HSQLDB. To work with the database independently from Studio, add the following task to build.gradle:

task startDb(type: CubaHsqlStart) {
    dbName = 'your_db_name'
}

After that, start HSQL server and create the database:

gradlew startDb createTestDb

Apparently, this is not required if you use a standalone DBMS like PostgreSQL.

Another option would be to use HSQL through in-memory connection specified by a URL like jdbc:hsqldb:file:path_to_db. The in-process connection has one disadvantage - it’s impossible to connect to the database under testing by an external SQL tool to find out what is actually saved.

Now about re-creating DB before each test.
We actually don’t do this. Instead we insert data for fixtures in setUp() methods and delete it in tearDown() using deleteRecord() methods defined in CubaTestCase. I’m afraid re-creating the database is much slower and can be an issue when running hundreds of tests at once. Besides, dropping the database will require closing connections to it. CubaTestCase uses a single instance of the Spring container for all running tests, so some manipulations with the datasource are needed.

In the middle of October, we are going to release the new platform version which will have improved test infrastructure. You will be able to write JUnit integration tests with @Test, @Rule, @ClassRule annotations and use shared or separate Spring containers more easily. After that, we will try to implement and document the scenario with re-creation the database, because it really makes sense for a case of the limited number of coarse-grained tests.

Hello Konstantin,
i tried to use the in memory variant myself by chaning the connection url to the following:

  
class DatabaseTestCase extends CubaTestCase {  
    @Override  
    protected void initDataSources() throws Exception {  
        Class.forName("org.hsqldb.jdbcDriver");  
        TestDataSource ds = new TestDataSource("jdbc:hsqldb:file/test123", "sa", "");  
        TestContext.getInstance().bind("java:comp/env/jdbc/CubaDS", ds);  
    }  






Which seems to create an in memory database correctly. The described situation that i can’t look into the db is ok for me at this point.
What is missing is, that the table structure is setup properly. I quess this is what had been done via "gradlew startDb createTestDb ", right? In case i want to migrate the db in the setup of the spec, to use the in memory variante, what do i have to do?
I’m not really sure, what is the right approach to it. Looking into the database could be valueable, but creating the db in the test setup and thus run your tests with a single command (and not start the db before) can be valuebale too.
Having a grails background, what they do in the “integration” test phase is that a h2 in memory db will be created and initializied. Then every testcase is encapsulated within a db transaction that is rolled back after each test execution. Due to this, the db does not have to be created for every test, but only for the whole test suite. On the other side it is pretty hard to test transactional behaviour of your software :slight_smile:

> every testcase is encapsulated within a db transaction
This is definitely too restrictive, at least for CUBA applications. For example, when you work through DataManager, it always starts a new transaction and receives/returns detached entities. And in general, I cannot imagine serious business logic encapsulated in one transaction only.
I’ll better try to implement the full drop/create scenario and come back to you with results.

Please look at the attached project. It has CustomerTest class which re-creates the whole database in setUp() method.
What I have done to make it work:

  • Added startTestDb and createTestDb tasks to build.gradle. They work with HSQL instance on non-standard port 9002 to not interfere with the instance run by Studio
  • Created com.company.integrationtests.support.BaseTest - this is a base class for integration tests in the project.
  • BaseTest instantiates datasource implementation directly from JDBC driver instead of pooling implementation recommended in the CUBA docs. This will enable dropping the database between tests.
  • BaseTest also adds my-test-app.properties and my-test-log4j.xml to the configuration.
  • my-test-app.properties file defines the cuba.dbDir app property pointing to the directory with the DB scripts
  • my-test-log4j.xml just makes the DbUpdater output less verbose by setting INFO level for it
  • com.company.integrationtests.core.CustomerTest class is a sample test which re-creates the database before each test execution:
protected void setUp() throws Exception {
    super.setUp();

    QueryRunner queryRunner = new QueryRunner(persistence.getDataSource());
    queryRunner.update("drop schema public cascade;");

    DbUpdater dbUpdater = AppBeans.get(DbUpdater.class);
    dbUpdater.updateDatabase();
}

Hello

I’m failing to find the attached project you mention in your comment?
What is the value of of the cuba.dbDir app proprerty that needs to be set for the DbUpdate.class? Im not sure what value to set as the db update/init scripts are not on the class path when running gradle test.