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 - 获取数据容器。