用途
该技能用于指导智能体在 Vaadin 25 中编写端到端(end-to-end)浏览器测试,适用场景是用户要求编写端到端测试等任务。TestBench 会在真实浏览器(Chrome、Firefox 等)中运行 Vaadin 应用,并提供 Java 测试 API,让测试以用户的方式与界面交互。它构建于 Selenium 之上,并针对 Vaadin 组件提供高层 API。
何时使用 TestBench(相对于 UI 单元测试)
适合使用端到端 TestBench 测试的场景包括:
- 关键用户路径——登录、结账、支付流程
- 客户端行为——JavaScript 功能、自定义 Web 组件
- 视觉回归——通过截图比对发现非预期的 UI 变化
- 跨浏览器测试——验证在 Chrome、Firefox、Safari 中的行为
- 与外部系统集成——SSO、OAuth 流程
其他情况优先使用 UI 单元测试(参见 ui-unit-testing 技能),其速度更快、更不易出现不稳定。
主要能力
核心概念
- Element:表示 DOM 元素的类,既可为内置 HTML 元素(如
<div>、<span>),也可为 Vaadin Web 组件(如<vaadin-button>、<vaadin-grid>)。Element 提供组件特有的交互方法。每个 Vaadin 组件都有对应的 Element 类:ButtonElement、TextFieldElement、GridElement、ComboBoxElement 等。 - ElementQuery:用于在页面上查找元素,通过
$()方法使用。支持按类型、ID、属性查找,以及嵌套查询与等待元素出现,相关方法包括id("id")、first()、last()、get(n)、all()、exists()、waitForFirst()、attribute("name", "value")。
编写测试
资料给出两种测试写法:JUnit 5(继承 BrowserTestBase,使用 @BrowserTest 注解)与 JUnit 4(继承 TestBenchTestCase,使用 @Before / @Test / @After)。示例均以登录流程演示:通过 $(TextFieldElement.class).id("username").setValue(...)、$(PasswordFieldElement.class).id("password").setValue(...)、$(ButtonElement.class).id("login").click() 操作组件,再以 assertTrue($(DivElement.class).id("dashboard").exists()) 断言跳转结果。
页面对象(Page Objects)
页面对象封装对特定视图或组件的交互,使测试方法保持整洁可维护;UI 变化时只需更新页面对象,而无需改每个测试。页面对象继承 TestBenchElement 并使用 @Element("tag-name") 注解,例如:
@Element("div")
@Attribute(name = "class", contains = "login-view")
public class LoginViewElement extends TestBenchElement {
public void login(String username, String password) {
$(TextFieldElement.class).id("username").setValue(username);
$(PasswordFieldElement.class).id("password").setValue(password);
$(ButtonElement.class).id("login").click();
}
public boolean isLoginFailed() {
return $(DivElement.class).attribute("class", "error").exists();
}
}
在测试中使用页面对象:
@BrowserTest
public void loginSuccess() {
LoginViewElement loginView = $(LoginViewElement.class).waitForFirst();
loginView.login("admin", "secret");
assertTrue($(DashboardViewElement.class).exists());
}
匹配策略:使用 @Attribute 将页面对象匹配到 DOM 元素,可按 class 属性匹配(多值属性用 contains),或使用 Attribute.SIMPLE_CLASS_NAME 按简单类名自动匹配(会移除 Element/PageObject 后缀)。
复杂组件操作
- Grid:
grid.getRowCount()获取行数,grid.getCell(0, 0).getText()获取单元格内容,grid.getRow(0).click()点击行,grid.scrollToRow(50)滚动到某行。 - ComboBox:
combo.openPopup()打开下拉,combo.selectByText("Finland")按文本选择。 - Dialog:对话框覆盖主内容,通过
$(DialogElement.class).waitForFirst()获取,再在对话框内部查找按钮并点击确认。
使用方式
- 不确定具体 API 细节时,使用 Vaadin MCP 工具(search_vaadin_docs)查阅最新文档,并将
vaadin_version设为 "25"、ui_language设为 "java"。 - 文档 MCP 不覆盖 TestBench API 本身。需要精确的 Element/ElementQuery 签名或源码时,应针对 com.vaadin 的 TestBench 构件(如 vaadin-testbench-core)使用 javadoc MCP(mcp__javadoc__*,未加载时可通过 ToolSearch javadoc 查找),而不是从 ~/.m2 解包 jar。
限制与注意事项
- TestBench 需要商业版 Vaadin 订阅。
- 资料未提供价格、安装命令、支持的浏览器版本范围等具体信息。
最佳实践(资料所列)
- 除最简单的测试外都使用页面对象,使测试可读可维护,让测试方法读起来像用户故事。
- 元素可能不是立即出现时(导航后、异步加载)使用
waitForFirst()而不是first()。 - 为关键组件指定 ID(如
component.setId("login-button")),便于在测试中查找,优先使用 ID 而非位置查询。 - 端到端测试聚焦关键路径,全面覆盖交给 UI 单元测试;端到端测试应验证旅程,而非每个边界情况。
- 在 CI 中使用无头浏览器运行测试,Chrome headless 是最可靠的选择。
- 不要 sleep,要 wait——用
waitForFirst()或waitUntil()替代Thread.sleep();显式等待更可靠也更快。 - 每个测试方法只测一个用户旅程;端到端测试成本高,每个测试应覆盖有意义的场景并保持相互独立。