4.8.2. 中间件集成测试
在中间件层,可以创建运行在具有完整功能的 Spring 容器里的测试用例,而且可以连接数据库。在这些测试类里面,可以运行中间件里面各细分层的代码,比如从 ORM 层到 Service 层。
首先,在 core 模块跟 src
目录平级创建 test
目录。然后在 Studio 里面重新创建 IDE 项目文件,以便在 IDE 里面运行测试。
平台自有的 TestContainer
类可以用来作为项目里面测试容器的基类。在 core 模块的 test
目录创建一个 TestContainer
类的子类,然后在这个子类的构造方法里面,重新定义加载 应用程序组件和 应用程序属性以及测试数据库连接的参数,示例:
public class SalesTestContainer extends TestContainer {
public SalesTestContainer() {
super();
appComponents = 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.
"test-app.properties",
"com/company/sales/test-app.properties");
dbDriver = "org.postgresql.Driver";
dbUrl = "jdbc:postgresql://localhost/sales_test";
dbUser = "cuba";
dbPassword = "cuba";
}
}
自定义 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):
public class CustomerLoadTest {
@ClassRule
public static SalesTestContainer cont = SalesTestContainer.Common.INSTANCE;
private Customer customer;
@Before
public void setUp() throws Exception {
customer = cont.persistence().createTransaction().execute(em -> {
Customer customer = cont.metadata().create(Customer.class);
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
方法里面使用,在测试完成后清理数据库。- 日志
测试容器根据平台提供的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
}