4.8.1. 中间件集成测试
中间件继承测试运行在具有完整功能的 Spring 容器里,而且可以连接数据库。在这些测试类里面,可以运行中间件里面各细分层的代码,比如从 ORM 层到 Service 层。
为了在测试中配置和启动中间件 Spring 容器,需要在项目中创建 com.haulmont.cuba.testsupport.TestContainer
的子类,并且在测试用例中使用其实例作为 JUnit Rule。
下面是容器类和快速开始中提到的 Sales 项目的一个集成测试的示例。所有的类必须在 core
模块的 test
目录。
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
里面定义的 createDb 任务来创建:
configure(coreModule) {
...
task createTestDb(dependsOn: assemble, description: 'Creates local Postgres database for tests', type: CubaDbCreation) {
dbms = 'postgres'
dbName = 'sales_test'
dbUser = 'cuba'
dbPassword = 'cuba'
}
这个测试容器应当在测试类里面作为 @ClassRule
注解指定的 JUnit 规则(rule):
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
方法里面使用,在测试完成后清理数据库。
日志
测试容器根据平台提供的 test-logback.xml
文件来配置日志。这个文件在 cuba-core-tests
工件的根目录。
可以通过以下方法配置测试的日志级别:
从平台的包里面拷贝
test-logback.xml
到项目core
模块test
根目录下,比如可以重命名为my-test-logback.xml
。在
my-test-logback.xml
里面配置 appenders 和 loggers。在测试容器里面添加一段静态初始化代码,这段代码通过设置
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);
}
}
}
还有,如果额外的数据库类型跟主数据库不一致,需要在 build.gradle
的 core
模块将数据库的驱动添加到 testRuntime
依赖中。示例:
configure(coreModule) {
// ...
dependencies {
// ...
testRuntime(hsql)
jdbc('org.postgresql:postgresql:9.4.1212')
testRuntime('org.postgresql:postgresql:9.4.1212') // add this
}