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

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

Для того, чтобы выполнять тесты из IDE, создайте каталог test в модуле core рядом с src. После этого пересоздайте проектные файлы IDE.

Платформа содержит класс TestContainer, который может быть использован в качестве базового для тестовых контейнеров приложения. Создайте наследника этого класса в каталоге test модуля core и в его конструкторе переопределите параметры загрузки компонентов и свойств приложения, а также параметры подключения к тестовой БД. Например:

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
                "cuba-app.properties",
                "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.
                "test-app.properties");
        dbDriver = "org.postgresql.Driver";
        dbUrl = "jdbc:postgresql://localhost/sales_test";
        dbUser = "cuba";
        dbPassword = "cuba";
    }
}

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

public class CustomerLoadTest {

    @ClassRule
    public static SalesTestContainer cont = new SalesTestContainer();

    private Customer customer;

    @Before
    public void setUp() throws Exception {
        customer = cont.persistence().createTransaction().execute(em -> {
            Customer customer = new Customer();
            customer.setName("testCustomer");
            em.persist(customer);
            return customer;
        });
    }

    @After
    public void tearDown() throws Exception {
        cont.deleteRecord(customer);
    }

    @Test
    public void test() {
        try (Transaction tx = cont.persistence().createTransaction()) {
            EntityManager em = cont.persistence().getEntityManager();
            TypedQuery<Customer> query = em.createQuery(
                "select c from sales$Customer c", Customer.class);
            List<Customer> list = query.getResultList();
            tx.commit();
            assertTrue(list.size() > 0);
        }
    }
}

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

Так как запуск контейнера занимает некоторое время, имеет смысл инициализировать контейнер один раз для тестов из нескольких (или всех) классов. Для этого создайте общий синглтон-экземпляр тестового контейнера, например:

public class SalesTestContainer extends TestContainer {

    public SalesTestContainer() {
        ...
    }

    public static class Common extends SalesTestContainer {

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

И используйте его в тестовых классах:

public class CustomerLoadTest {

    @ClassRule
    public static SalesTestContainer cont = SalesTestContainer.Common.INSTANCE;

    ...
}

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

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

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

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