4.8.1. Middleware Integration Tests

Middleware integration tests run in a fully functional Spring container connected to the database. In such tests, you can run code on all layers of the middleware, from services down to ORM.

In order to configure and start the middleware Spring container in tests, create a subclass of the com.haulmont.cuba.testsupport.TestContainer base class in your project and use its instance in tests as a JUnit Rule.

Below is an example of the container class and an integration test for the Sales project described in Quick Start. All classes must be located in the test directory of the core module.

package com.company.sales;

import com.haulmont.cuba.testsupport.TestContainer;

import java.util.ArrayList;
import java.util.Arrays;

public class SalesTestContainer extends TestContainer {

    public SalesTestContainer() {
        super();
        appComponents = new ArrayList<>(Arrays.asList(
                "com.haulmont.cuba"
                // add CUBA premium add-ons here
                // "com.haulmont.bpm",
                // "com.haulmont.charts",
                // "com.haulmont.fts",
                // "com.haulmont.reports",
                // and custom app components if any
        ));
        appPropertiesFiles = Arrays.asList(
                // List the files defined in your web.xml
                // in appPropertiesConfig context parameter of the core module
                "com/company/sales/app.properties",
                // Add this file which is located in CUBA and defines some properties
                // specifically for test environment. You can replace it with your own
                // or add another one in the end.
                "com/haulmont/cuba/testsupport/test-app.properties");
        initDbProperties();
    }

    private void initDbProperties() {
        dbDriver = "org.postgresql.Driver";
        dbUrl = "jdbc:postgresql://localhost/sales_test";
        dbUser = "cuba";
        dbPassword = "cuba";
    }

    public static class Common extends SalesTestContainer {

        // A common singleton instance of the test container which is initialized once for all tests
        public static final SalesTestContainer.Common INSTANCE = new SalesTestContainer.Common();

        private static volatile boolean initialized;

        private Common() {
        }

        @Override
        public void before() throws Throwable {
            if (!initialized) {
                super.before();
                initialized = true;
            }
            setupContext();
        }

        @Override
        public void after() {
            cleanupContext();
            // never stops - do not call super
        }
    }
}

An example of the custom test-app.properties file:

cuba.webContextName = app-core
sales.someProperty = someValue

We recommend using a separate test database, which can be created, for example, by the following Gradle task defined in build.gradle:

configure(coreModule) {
...
    task createTestDb(dependsOn: assemble, description: 'Creates local Postgres database for tests', type: CubaDbCreation) {
        dbms = 'postgres'
        dbName = 'sales_test'
        dbUser = 'cuba'
        dbPassword = 'cuba'
    }

The test container should be used in test classes as a JUnit rule specified by the @ClassRule annotation:

package com.company.sales;

import com.company.sales.entity.Customer;
import com.haulmont.cuba.core.global.*;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

public class CustomerTest {

    // Using the common singleton instance of the test container which is initialized once for all tests
    @ClassRule
    public static SalesTestContainer cont = SalesTestContainer.Common.INSTANCE;

    private Metadata metadata;

    @Before
    public void setUp() throws Exception {
        metadata = cont.metadata();
    }

    @Test
    public void testCreateCustomer() throws Exception {
        // Get a managed bean (or service) from container
        DataManager dataManager = AppBeans.get(DataManager.class);

        // Create new Customer
        Customer customer = metadata.create(Customer.class);
        customer.setName("Test customer");

        // Save the customer to the database
        dataManager.commit(customer);

        // Load the customer by ID
        Customer loaded = dataManager.load(
                LoadContext.create(Customer.class).setId(customer.getId()).setView(View.LOCAL));

        assertNotNull(loaded);
        assertEquals(customer.getName(), loaded.getName());

        // Remove the customer
        dataManager.remove(loaded);
    }
}
Useful container methods

The TestContainer class contains the following methods that can be used in the test code (see the CustomerLoadTest example above):

  • persistence() – returns the reference to the Persistence interface.

  • metadata() – returns the reference to the Metadata interface.

  • deleteRecord() – this set of overloaded methods is aimed to be used in @After methods to clean up the database after tests.

Logging

The test container sets up logging according to the test-logback.xml file provided by the platform. It is contained in the root of the cuba-core-tests artifact.

If you want to configure logging levels for your tests, do the following:

  • Copy test-logback.xml from the platform artifact to the root of the test folder of your project’s core module, e.g. as my-test-logback.xml.

  • Configure appenders and loggers in my-test-logback.xml.

  • Add a static initializer to your test container to specify the location of your logback configuration file in the logback.configurationFile system property:

    public class MyTestContainer extends TestContainer {
    
        static {
            System.setProperty("logback.configurationFile", "my-test-logback.xml");
        }
    
        // ...
    }
Additional Data Stores

If your project uses additional data stores, you should create corresponding JDBC data sources in your test container. For example, if you have mydb datastore which is a PostgreSQL database, add the following method to the test container class:

public class MyTestContainer extends TestContainer {
    // ...

    @Override
    protected void initDataSources() {
        super.initDataSources();
        try {
            Class.forName("org.postgresql.Driver");
            TestDataSource mydbDataSource = new TestDataSource(
                    "jdbc:postgresql://localhost/mydatabase", "db_user", "db_password");
            TestContext.getInstance().bind(
                    AppContext.getProperty("cuba.dataSourceJndiName_mydb"), mydbDataSource);
        } catch (ClassNotFoundException | NamingException e) {
            throw new RuntimeException("Error initializing datasource", e);
        }
    }
}

Also, if the additional database type is different from the main one, you should add its driver as the testRuntime dependency to the core module in build.gradle, for example:

configure(coreModule) {
    // ...
    dependencies {
        // ...
        testRuntime(hsql)
        jdbc('org.postgresql:postgresql:9.4.1212')
        testRuntime('org.postgresql:postgresql:9.4.1212') // add this
    }