4.9.1. 中间件集成测试

中间件继承测试运行在具有完整功能的 Spring 容器里,而且可以连接数据库。在这些测试类里面,可以运行中间件里面各细分层的代码,比如从 ORM 层到 Service 层。

Studio 创建新项目之后,可以在 core 模块的包内找到两个类:一个测试容器类和一个测试示例类。测试容器类会启动中间件的 Spring 容器,该容器用来做测试。测试示例类使用这个容器并演示如何用实体测试一些操作。

我们看看生成的测试容器类,怎么能满足我们的需求。

该类必须继承 CUBA 提供的 TestContainer 类。在构造器中,需要做下面这些事:

  • 需要在 appComponents 列表中添加项目中使用的 应用程序组件(扩展插件)。

  • 如果需要,在 appPropertiesFiles 列表指定附加的应用程序属性文件。

  • 调用 autoConfigureDataSource() 方法使用来自应用程序属性或者 context.xml 的信息初始化测试数据源。

生成的测试容器会提供与应用程序相同的数据库连接,所以即使修改了类型或者 JDBC 数据源的定义,测试用例还是会使用主数据存储运行。

使用同一个数据库进行测试和运行应用程序有一个弊端:手动输入的数据会干扰测试数据并有可能破坏测试用例的执行。避免的方法就是为测试单独配置一个数据库。我们建议使用和主数据存储同类型的数据库作为测试数据库,以便使用同一组 数据库迁移脚本。下面是在本地 PostgreSQL 配置测试数据库的一个示例。

首先,在 build.gradle 添加测试数据库创建的 任务

configure(coreModule) {
    // ...
    task createTestDb(dependsOn: assembleDbScripts, type: CubaDbCreation) {
        dbms = 'postgres'
        host = 'localhost'
        dbName = 'demo_test'
        dbUser = 'cuba'
        dbPassword = 'cuba'
    }

然后在测试源码的根包(比如 modules/core/test/com/company/demo/test-app.properties)内创建 test-app.properties 文件,并制定测试数据库的连接属性:

cuba.dataSource.host = localhost
cuba.dataSource.dbName = demo_test
cuba.dataSource.username = cuba
cuba.dataSource.password = cuba

添加该文件至测试容器的 appPropertiesFiles 列表:

public class DemoTestContainer extends TestContainer {

    public DemoTestContainer() {
        super();
        appComponents = Arrays.asList(
                "com.haulmont.cuba"
        );
        appPropertiesFiles = Arrays.asList(
                "com/company/demo/app.properties",
                "com/haulmont/cuba/testsupport/test-app.properties",
                "com/company/demo/test-app.properties" // your test properties
        );
        autoConfigureDataSource();
    }

运行测试用例之前,执行这个任务创建测试数据库:

./gradlew createTestDb

这个测试容器应当在测试类里面作为 @RegisterExtension 注解指定的 JUnit 5 扩展:

package com.company.demo.core;

import com.company.demo.DemoTestContainer;
import com.company.demo.entity.Customer;
import com.haulmont.cuba.core.entity.contracts.Id;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.DataManager;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class CustomerTest {

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

    static DataManager dataManager;

    @BeforeAll
    static void beforeAll() {
        // Get a bean from the container
        dataManager = AppBeans.get(DataManager.class);
    }

    @Test
    void testCreateLoadRemove() {
        Customer customer = cont.metadata().create(Customer.class);
        customer.setName("c1");

        Customer committedCustomer = dataManager.commit(customer);
        assertEquals(customer, committedCustomer);

        Customer loadedCustomer = dataManager.load(Id.of(customer)).one();
        assertEquals(customer, loadedCustomer);

        dataManager.remove(loadedCustomer);
    }
}
几个有用的测试容器方法

TestContainer 类包含了以下几个方法,可以在测试类里面使用(参考上面的 CustomerTest 例子):

  • persistence() – 返回 Persistence 接口的引用。

  • metadata() – 返回 Metadata 接口的引用。

  • deleteRecord() – 这一组重载方法的目的是在 @After 方法里面使用,在测试完成后清理数据库。

还有,可以按照上面例子中的方法使用 AppBeans.get() 静态方法获取任何 bean。

日志

测试容器根据平台提供的 test-logback.xml 文件来配置日志。

可以通过以下方法配置测试的日志级别:

  • 在项目 core 模块的 test 目录内创建 my-test-logback.xml 文件。

  • my-test-logback.xml 里面配置 appenders 和 loggers。可以从 cuba-core-tests 工件内的 test-logback.xml 文件复制默认的文件内容。

  • 在测试容器里面添加一段静态初始化代码,这段代码通过设置 logback.configurationFile 这个系统属性来指定日志配置文件的位置:

    public class DemoTestContainer extends TestContainer {
    
        static {
            System.setProperty("logback.configurationFile", "com/company/demo/my-test-logback.xml");
        }
附加数据存储

如果项目使用了附加数据存储,并且附加数据库类型与主数据库不同,需要在 build.gradlecore 模块将数据库的驱动添加到 testRuntime 依赖中。示例:

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