4.9.2. Web 集成测试
Web 集成测试运行在 Web 客户端 block 的 Spring 容器中。测试容器独立于中间件工作,因为框架会自动为所有中间件服务创建桩代码。测试基础设施由 com.haulmont.cuba.web.testsupport
及其内部包的下列类组成:
TestContainer
- Spring 容器的包装器,用来作为项目特定容器的基类。TestServiceProxy
- 为中间件服务提供默认的桩代码。该类可以用来注册为特定用例 mock 的服务,参考其mock()
静态方法。DataServiceProxy
-DataManager
的默认桩代码。其包含一个commit()
方法的实现,能模拟真正的数据存储的行为:能让新实体 detach,增加实体版本,等等。加载方法返回 null 和空集合。TestUiEnvironment
- 提供一组方法用来配置和获取TestContainer
。该类的实例在测试中需要作为 JUnit 5 的扩展来使用。TestEntityFactory
- 测试中为方便创建实体实例的工厂。可以通过TestContainer
获取工厂。
尽管框架为服务提供了默认桩代码,但是在测试中也许需要自己创建服务的 mock。要创建 mock,可以使用任何 mocking 框架,通过添加其为依赖即可,如上节所说。服务的 mock 均使用 TestServiceProxy.mock()
方法注册。
Web 集成测试容器示例
在 web
模块创建 test
目录。然后在 test
目录合适的包内创建项目的测试容器类:
package com.company.demo;
import com.haulmont.cuba.web.testsupport.TestContainer;
import java.util.Arrays;
public class DemoWebTestContainer extends TestContainer {
public DemoWebTestContainer() {
appComponents = Arrays.asList(
"com.haulmont.cuba"
// add CUBA add-ons and custom app components here
);
appPropertiesFiles = Arrays.asList(
// List the files defined in your web.xml
// in appPropertiesConfig context parameter of the web module
"com/company/demo/web-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/web/testsupport/test-web-app.properties"
);
}
public static class Common extends DemoWebTestContainer {
// A common singleton instance of the test container which is initialized once for all tests
public static final DemoWebTestContainer.Common INSTANCE = new DemoWebTestContainer.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
}
}
}
UI 界面测试示例
下面是 Web 集成测试的示例,在一些用户操作之后检查了编辑实体的状态。
package com.company.demo.customer;
import com.company.demo.DemoWebTestContainer;
import com.company.demo.entity.Customer;
import com.company.demo.web.screens.customer.CustomerEdit;
import com.haulmont.cuba.gui.Screens;
import com.haulmont.cuba.gui.components.Button;
import com.haulmont.cuba.gui.screen.OpenMode;
import com.haulmont.cuba.web.app.main.MainScreen;
import com.haulmont.cuba.web.testsupport.TestEntityFactory;
import com.haulmont.cuba.web.testsupport.TestEntityState;
import com.haulmont.cuba.web.testsupport.TestUiEnvironment;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import java.util.Collections;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
public class CustomerEditInteractionTest {
@RegisterExtension
TestUiEnvironment environment =
new TestUiEnvironment(DemoWebTestContainer.Common.INSTANCE).withUserLogin("admin"); (1)
private Customer customer;
@BeforeEach
public void setUp() throws Exception {
TestEntityFactory<Customer> customersFactory =
environment.getContainer().getEntityFactory(Customer.class, TestEntityState.NEW);
customer = customersFactory.create(Collections.emptyMap()); (2)
}
@Test
public void testGenerateName() {
Screens screens = environment.getScreens(); (3)
screens.create(MainScreen.class, OpenMode.ROOT).show(); (4)
CustomerEdit customerEdit = screens.create(CustomerEdit.class); (5)
customerEdit.setEntityToEdit(customer);
customerEdit.show();
assertNull(customerEdit.getEditedEntity().getName());
Button generateBtn = (Button) customerEdit.getWindow().getComponent("generateBtn"); (6)
customerEdit.onGenerateBtnClick(new Button.ClickEvent(generateBtn)); (7)
assertEquals("Generated name", customerEdit.getEditedEntity().getName());
}
}
1 | - 定义带共享容器和带有 admin 的用户会话存根的测试环境。 |
2 | - 创建 new 状态的实体实例。 |
3 | - 从环境获取 Screens 基础设施对象。 |
4 | - 打开主界面,打开应用程序界面必须的步骤。 |
5 | - 创建、初始化并打开实体编辑界面。 |
6 | - 获取 Button 组件。 |
7 | - 创建一个点击事件,并以调用控制器方法的方式响应点击操作。 |
测试在界面加载数据的示例
下面是一个 web 集成测试的示例,检查加载数据的正确性。
package com.company.demo.customer;
import com.company.demo.DemoWebTestContainer;
import com.company.demo.entity.Customer;
import com.company.demo.web.screens.customer.CustomerEdit;
import com.haulmont.cuba.core.app.DataService;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.LoadContext;
import com.haulmont.cuba.gui.Screens;
import com.haulmont.cuba.gui.model.InstanceContainer;
import com.haulmont.cuba.gui.screen.OpenMode;
import com.haulmont.cuba.gui.screen.UiControllerUtils;
import com.haulmont.cuba.web.app.main.MainScreen;
import com.haulmont.cuba.web.testsupport.TestEntityFactory;
import com.haulmont.cuba.web.testsupport.TestEntityState;
import com.haulmont.cuba.web.testsupport.TestUiEnvironment;
import com.haulmont.cuba.web.testsupport.proxy.TestServiceProxy;
import mockit.Delegate;
import mockit.Expectations;
import mockit.Mocked;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class CustomerEditLoadDataTest {
@RegisterExtension
TestUiEnvironment environment =
new TestUiEnvironment(DemoWebTestContainer.Common.INSTANCE).withUserLogin("admin"); (1)
@Mocked
private DataService dataService; (1)
private Customer customer;
@BeforeEach
public void setUp() throws Exception {
new Expectations() {{ (2)
dataService.load((LoadContext<? extends Entity>) any);
result = new Delegate() {
Entity load(LoadContext lc) {
if ("demo_Customer".equals(lc.getEntityMetaClass())) {
return customer;
} else
return null;
}
};
}};
TestServiceProxy.mock(DataService.class, dataService); (3)
TestEntityFactory<Customer> customersFactory =
environment.getContainer().getEntityFactory(Customer.class, TestEntityState.DETACHED);
customer = customersFactory.create(
"name", "Homer", "email", "homer@simpson.com"); (4)
}
@AfterEach
public void tearDown() throws Exception {
TestServiceProxy.clear(); (5)
}
@Test
public void testLoadData() {
Screens screens = environment.getScreens();
screens.create(MainScreen.class, OpenMode.ROOT).show();
CustomerEdit customerEdit = screens.create(CustomerEdit.class);
customerEdit.setEntityToEdit(customer);
customerEdit.show();
InstanceContainer customerDc = UiControllerUtils.getScreenData(customerEdit).getContainer("customerDc"); (6)
assertEquals(customer, customerDc.getItem());
}
}
1 | - 使用 JMockit framework 定义数据服务 mock。 |
2 | - 定义 mock 行为。 |
3 | - 注册 mock。 |
4 | - 创建 detached 状态的实体实例。 |
5 | - 测试完成后移除 mock。 |
6 | - 获取数据容器。 |