4.8.1. Интеграционные тесты Middleware

Интеграционные тесты Middleware выполняются в полнофункциональном контейнере Spring с подключением к базе данных. В тестах такого типа можно выполнять код любого слоя внутри Middleware - от сервисов до ORM.

Для конфигурирования и запуска контейнера Spring в тестах среднего слоя создайте в приложении наследника базовового класса com.haulmont.cuba.testsupport.TestContainer и используйте его экземпляр в тестах в качестве JUnit Rule.

Ниже приведен пример класса контейнера и интеграционного теста для проекта Sales, описанного в разделе Быстрый старт. Все классы должны находится в каталоге test модуля core.

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
        }
    }
}

Пример собственного файлв test-app.properties:

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

В качестве базы данных рекомендуется использовать отдельную тестовую БД, которую можно создавать, например, следующей задачей в 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'
    }

Тестовый контейнер используется в классах тестов в качестве JUnit rule, указанного с помощью аннотации @ClassRule:

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);
    }
}
Полезные методы тестового контейнера

Класс TestContainer содержит следующие методы, которые можно использовать в коде тестов (см. пример CustomerLoadTest выше):

  • persistence() - возвращает ссылку на интерфейс Persistence.

  • metadata() - возвращает ссылку на интерфейс Metadata.

  • deleteRecord() - этот набор перегруженных методов предназначен для использования в @After методах для удаления тестовых объектов из БД.

Логирование

Класс TestContainer настраивает логирование в соответствие с файлом test-logback.xml, предоставляемым платформой. Данный файл содержится в артефакте cuba-core-tests.

Для того, чтобы настроить уровни логирования в своих тестах, необходимо выполнить следующее:

  • Скопируйте файл test-logback.xml из артефакта платформы в корень каталога test модуля core проекта, например как my-test-logback.xml.

  • Сконфигурируйте параметры логирования в my-test-logback.xml.

  • Добавьте блок статической инициализации в класс тестового контейнера проекта и укажите местоположение файла конфигурации Logback в системном свойстве logback.configurationFile:

    public class MyTestContainer extends TestContainer {
    
        static {
            System.setProperty("logback.configurationFile", "my-test-logback.xml");
        }
    
        // ...
    }
Дополнительные хранилища

Если в вашем проекте используются дополнительные хранилища, необходимо создать соответствующие источники данных JDBC в тестовом контейнере. Например, если у вас есть хранилище mydb, являющееся базой данных PostgreSQL, добавьте следующий метод в класс тестового контейнера:

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);
        }
    }
}

Кроме того, если тип дополнительной базы данных отличается от основной, необходимо добавить ее драйвер как testRuntime зависимость модуля core в build.gradle, например:

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