Apache DolphinScheduler E2E 测试参与指南:从认领 Issue、编写用例到提交 Pull Request

Apache DolphinScheduler E2E 测试参与指南:从认领 Issue、编写用例到提交 Pull Request Apache DolphinScheduler E2E 测试参与指南从认领 Issue、编写用例到提交 Pull Request【免费下载链接】dolphinschedulerApache DolphinScheduler is the modern data orchestration platform. Agile to create high performance workflow with low-code项目地址: https://gitcode.com/GitHub_Trending/dol/dolphinscheduler本篇技术指南以 Apache DolphinScheduler 社区 E2E端到端自动化测试为线索完整讲解如何寻找待完成的 E2E 测试 Issue、理解用例表格字段、基于 Selenium 与 Page Object Model 编写测试代码并结合仓库源码剖析dolphinscheduler-e2e模块的底层运行机制Testcontainers、DolphinScheduler注解、浏览器录制等。读完本文你将掌握一套认领任务 → 编写用例 → 本地运行 → 提交 PR的完整参与路径可直接上手为 DolphinScheduler 贡献 E2E 测试。一、为什么要做 E2E 测试黑盒视角下的系统验证E2E 测试的主要目的是通过模拟真实的用户场景验证被测系统及其组件的集成性和数据完整性从而扩展测试范围确保系统健康稳定在一定程度上减少测试工作量和成本。简单来说E2E 测试就是把程序当作黑盒子以用户的视角对真实系统的访问行为进行仿真向系统输入用户行为/模拟数据后观察能否得到预期结果。1.1 E2E 测试与单元测试的区别单元测试通常需要测试参数、参数类型、参数值、参数数量、返回值、抛出错误等目的在于保证特定函数在任何情况下都能稳定可靠地完成工作。单元测试隐含的假设是只要所有函数都正常工作整个产品就能正常工作。E2E 测试并不强调覆盖全部使用场景它关注的是一条完整的操作链是否能够完成。对于 Web 前端而言还关注界面布局、内容信息是否符合预期。以登录界面为例E2E 测试关注用户能否正常输入、正常登录登录失败时能否正确显示错误信息。至于输入不合法内容是否被处理并不是关注的重点。基于这一理念社区决定为 DolphinScheduler 增加 E2E 自动化测试当前社区的 E2E 测试尚未达到完全覆盖因此编写了本篇参与指南目的在于引导更多贡献者参与进来。1.2 底层框架Selenium 与 WebDriverDolphinScheduler 的 E2E 测试基于 Selenium 开源测试工具用于在 Web 浏览器上执行自动化测试。该框架的核心机制是WebDriver一个 API 和协议定义了语言中立的接口用于控制 Web 浏览器的行为。每个浏览器都有一个特定的 WebDriver 实现称为驱动程序驱动程序负责委派给浏览器的操作并处理 Selenium 与浏览器之间的通信。Selenium 框架通过一个面向用户的界面将所有部分连接在一起允许透明地使用不同的浏览器后端从而实现跨浏览器和跨平台自动化。在 DolphinScheduler 的 E2E 工程中测试运行在 Selenium 的 standalone Chrome 容器中这一点可以在源码 DolphinSchedulerExtension.java 的setBrowserContainerByOsName方法中得到印证——非 ARM 平台使用selenium/standalone-chrome镜像M1 芯片则替换为seleniarm/standalone-chromium镜像。二、如何寻找对应的 E2E 测试 Issue社区把 E2E 需要测试的页面整理成了相关 Issue主要分为Project Management项目管理、Resource Center资源中心、DataSource数据源、Security Center安全中心四个页面。2.1 检索方式与认领流程贡献者可以通过 GitHub 在 issue 列表中搜索e2e test cases即可找到对应的任务以is:issue is:open e2e test cases过滤在每个对应的 Issue 中社区都列出了需要测试的内容和期望的结果可以在 Description 中查看。进入页面后可以根据自身兴趣选择对应的 Issue例如参与 Security Center 的测试在对应的 Issue 下留言自己想测试的案例即可。2.2 Issue 中测试用例表格的字段含义每个 Issue 内以表格形式组织测试用例各列含义如下字段含义参与时注意Task status测试任务状态若该测试已完成则视为 finish作为贡献者需要寻找尚未完成的测试number测试案例的序号认领时说明具体编号function module需要测试的功能模块一个功能模块包含多个测试案例—test point具体的测试点例如页面中的按钮点击、页面跳转功能—priority测试案例的优先级推荐寻找优先级高的案例service测试过程中需要启动的服务例如 start API servicetest steps每个测试案例需要操作的测试步骤编写用例时逐条映射为代码操作expected results所期望的测试结果编写断言assertion的依据actual results实际测试的结果测试失败时记录remarks测试过程中需要注意的点—认领任务后即可进入下一阶段编写测试代码。三、如何编写 E2E 测试代码3.1 页面模型Page Object ModelDolphinScheduler 的 E2E 测试遵循 Page Object Model页面对象模型。项目中的页面模型统一放在dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/目录下包括LoginPage、common/NavBarPage、security/*、datasource/DataSourcePage、project/*等。下面以登录页为例展示模型的定义方式对应指南原始示例package org.apache.dolphinscheduler.e2e.pages; import org.apache.dolphinscheduler.e2e.pages.common.NavBarPage; import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import lombok.Getter; import lombok.SneakyThrows; Getter public final class LoginPage extends NavBarPage { FindBy(id inputUsername) private WebElement inputUsername; FindBy(id inputPassword) private WebElement inputPassword; FindBy(id btnLogin) private WebElement buttonLogin; public LoginPage(RemoteWebDriver driver) { super(driver); } SneakyThrows public TenantPage login(String username, String password) { inputUsername().sendKeys(username); inputPassword().sendKeys(password); buttonLogin().click(); new WebDriverWait(driver, 10) .until(ExpectedConditions.urlContains(/#/security)); return new TenantPage(driver); } }关键点说明只声明关心的元素测试过程中只针对所需关注的元素进行测试而非页面中的所有元素所以在登录页面只对用户名、密码和登录按钮等元素进行声明。通过FindBy定位元素使用 Selenium 提供的FindBy接口可以按id、className、css选择器、tagName甚至xpath来定位 Vue 页面中对应的元素。封装复用方法而非直接操作元素测试中不会直接操作元素一般选择封装对应的方法以达到复用效果。例如想要登录直接传入用户名和密码调用login()方法登录完成后跳转到目标页面并返回对应页面对象。需要说明的是随着前端 UI 的演进当前仓库中 LoginPage.java 已改用基于 class 的组合定位FindBys组合className与tagName并在登录前先点击语言切换按钮、等待跳转到/homeFindBys({ FindBy(className input-user-name), FindBy(tagName input) }) private WebElement inputUsername; FindBys({ FindBy(className input-password), FindBy(tagName input) }) private WebElement inputPassword; FindBy(className btn-login) private WebElement buttonLogin; FindBy(className n-switch__button) private WebElement buttonSwitchLanguage;3.2 导航栏跳转goToNav 与 goToTabDolphinScheduler 的 UI 包含顶部的横向导航栏Project / Resources / Datasource / Security以及各模块内部的左侧纵向菜单。E2E 工程对此做了两级封装第一级NavBarPage.goToNav()实现横向导航跳转。在 NavBarPage.java 中通过goToNav(ClassT nav)方法支持跳转到项目管理ProjectPage、安全中心SecurityPage、资源中心ResourcePage和数据源DataSourcePagepublic T extends NavBarItem T goToNav(ClassT nav) { if (nav ProjectPage.class) { WebDriverWaitFactory.createWebDriverWait(driver) .until(ExpectedConditions.elementToBeClickable(projectTab)); ((JavascriptExecutor) driver).executeScript(arguments[0].click();, projectTab()); WebDriverWaitFactory.createWebDriverWait(driver) .until(ExpectedConditions.urlContains(/projects/list)); return nav.cast(new ProjectPage(driver)); } // SecurityPage / ResourcePage / DataSourcePage 同理 throw new UnsupportedOperationException(Unknown nav bar); }其实现要点是先用显式等待WebDriverWait确保菜单元素可点击再通过JavascriptExecutor执行点击随后等待 URL 命中对应路由如/security/tenant-manage最后把当前页面对象强转返回给调用方形成流畅的链式调用。第二级SecurityPage.goToTab()实现安全中心左侧菜单跳转。在 SecurityPage.java 中goToTab(ClassT tab)支持租户管理TenantPage、用户管理UserPage、Worker 分组管理WorkerGroupPage、队列管理QueuePage、环境管理EnvironmentPage、集群管理ClusterPage、Token 管理TokenPage、K8s 命名空间管理NamespacePage等页面的跳转每个分支都会等待对应路由出现后才返回页面对象。从源码结构看goToNav和goToTab的设计把页面跳转 等待 返回页面对象封装为可复用能力测试用例只需要一行链式调用即可完成复杂的多级导航这正是 Page Object Model 在大型管理后台中的典型应用。3.3 测试用例的骨架注解、注入与断言每一个测试案例都需要引入对应的文件作为前置环境。E2E 测试环境基于Testcontainers docker compose搭建见 dolphinscheduler-e2e/README.md 的 Test Environment Setup 一节测试类通过DolphinScheduler(composeFiles ...)注解声明需要的 docker-compose 文件。该注解定义于 DolphinScheduler.java它组合了 JUnit 5 的Testcontainers、TestMethodOrder(OrderAnnotation.class)按Order执行以及自定义的DolphinSchedulerExtension。以租户管理测试 TenantE2ETest.java 为例完整的用例结构如下DolphinScheduler(composeFiles docker/basic/docker-compose.yaml) DisableIfTestFails class TenantE2ETest { private static final String tenant System.getProperty(user.name); private static final String editDescription This is a test; private static RemoteWebDriver browser; BeforeAll public static void setup() { new LoginPage(browser) .login(admin, dolphinscheduler123) .goToNav(SecurityPage.class) .goToTab(TenantPage.class); } Test Order(10) void testCreateTenant() { final TenantPage page new TenantPage(browser); page.create(tenant); Awaitility.await().untilAsserted(() - assertThat(page.tenantList()) .as(Tenant list should contain newly-created tenant) .extracting(WebElement::getText) .anyMatch(it - it.contains(tenant))); } Test Order(20) void testCreateDuplicateTenant() { final TenantPage page new TenantPage(browser); page.create(tenant); Awaitility.await().untilAsserted(() - assertThat( browser.findElement(By.tagName(body)).getText()).contains(already exists)); page.tenantForm().buttonCancel().click(); } Test Order(30) void testUpdateTenant() { TenantPage page new TenantPage(browser); page.update(tenant, editDescription); Awaitility.await().untilAsserted(() - { browser.navigate().refresh(); assertThat(page.tenantList()).extracting(WebElement::getText).anyMatch(it - it.contains(tenant)); }); } Test Order(40) void testDeleteTenant() { final TenantPage page new TenantPage(browser); page.delete(tenant); Awaitility.await().untilAsserted(() - { browser.navigate().refresh(); assertThat(page.tenantList()).noneMatch(it - it.getText().contains(tenant)); }); } }这段代码对应着原指南中的几个核心编写范式BeforeAll setup()准备工作每个测试案例开始之前都需要登录用户、跳转到对应页面。由于浏览器由测试框架注入这里的browser在beforeAll回调中就已由DolphinSchedulerExtension通过反射写入静态字段见 DolphinSchedulerExtension.java。Order()注解用于模块化并确认测试顺序。例如租户测试按创建 → 创建重复 → 修改 → 删除依次执行编号 10/20/30/40后面的用例依赖前面用例产生的数据。Awaitility.await().untilAsserted(...)异步断言UI 测试中页面加载和操作完成都需要时间因此使用 Awaitility 等待断言条件满足。测试框架在beforeAll中设置了默认超时 120 秒、轮询间隔 500 毫秒见 DolphinSchedulerExtension.java并对列表增删改结果逐一断言若断言返回 true 则表示操作成功如创建租户成功。覆盖增删改查基础功能E2E 测试单机模式主要用于检验例如增删改查的基本功能后期如需做集群验证例如不同服务之间的协作或者各服务之间的通讯机制可参考 docker-compose.yml 进行配置。WebDriverWaitFactory见 WebDriverWaitFactory.java则统一封装了显式等待的默认超时10 秒与轮询间隔500 毫秒页面模型中的元素可点击URL 命中等等待条件都经由它创建。仓库中已有的大量用例可作为编写参考分布在 dolphinscheduler-e2e-case 目录ProjectE2ETest、FileManageE2ETest、ClusterE2ETest、EnvironmentE2ETest、UserE2ETest、QueueE2ETest、WorkerGroupE2ETest、TokenE2ETest、多个数据源测试MySQL/PostgreSQL/Hive/ClickHouse/DolphinDB/SqlServer以及工作流与任务相关测试WorkflowE2ETest、ShellTaskE2ETest、PythonTaskE2ETest等。四、在本地运行 E2E 测试运行测试前需要先搭建 DolphinScheduler 本地开发环境可参考 开发手册standalone 模式推荐启动org.apache.dolphinscheduler.StandaloneServer前端在dolphinscheduler-ui目录执行pnpm install pnpm run dev默认账户密码为admin/dolphinscheduler123。在本地运行时需要在测试运行配置中添加 VM Options 参数4.1 三种运行模式模式参数前提条件说明本地模式不使用 Docker-Dlocaltrue需要在本地启动前端和后端服务便于连接本地、边改 UI 边验证对应DolphinSchedulerExtension中的LOCAL_MODE端口为 5173本地模式使用 Docker无只需本地安装 Docker通过 Testcontainers 启动 docker-compose 与浏览器容器Mac M1 芯片-Dm1_chiptrue需要安装并运行 Docker Desktop for Mac使用支持 ARM64 的seleniarm/standalone-chromium容器以上逻辑可在 DolphinSchedulerExtension.java 中找到对应实现LOCAL_MODE对应System.getProperty(local) trueM1_CHIP_FLAG对应System.getProperty(m1_chip) true。4.2 运行步骤与注意事项将dolphinscheduler-e2e/pom.xml加入 Maven 工程由于它不参与项目整体编译因此不在主项目中。在 IDE 中直接运行测试类如org.apache.dolphinscheduler.e2e.cases.UserE2ETest。测试结束后运行过程的视频会以MP4 格式保存到本地临时目录如/var/folders/hf/123/T/record-3123/PASSED-...mp4。录屏功能由 DolphinSchedulerExtension.java 中的setRecordPath控制可通过环境变量RECORDING_PATH自定义录制目录否则写入java.io.tmpdir下的record-*临时目录Constants.java见 Constants.java中定义了宿主机与浏览器容器内的 Chrome 下载目录映射。超时调整本地运行过程中如果出现连接超时可增大加载时间建议设置为 30 秒及以上若运行环境性能较弱也可在测试类中适当调大Awaitility与WebDriverWait的超时配置。五、如何提交 Pull Request参与开源社区的形式多种多样不限于 issue、pull request 和翻译等等。在参与 E2E 测试的过程中首先要求贡献者了解简单的提交 Pull Request 的流程可参考 Pull Request 须知下面提炼其核心规范。5.1 Pull Request 标题格式标题格式为[Pull Request 类型- Issue 号][模块名] Pull Request 描述类型与含义对应关系如下以 Issue 号 3333 为例Issue 类型Pull Request 类型样例假设 Issue 号为 3333FeatureFeature[Feature-3333][server] Implement xxxBugFix[Fix-3333][ui] Fix xxxImprovementImprovement[Improvement-3333][alert] Improve the performance of xxxTestTest[Test-3333][api] Add the e2e test of xxxDocDoc[Doc-3333] Improve xxxE2EE2E[E2E-3333] Implement xxxCICI[CI] Improve xxxChoreChore[Chore] Improve xxx其中Issue 号指当前 Pull Request 对应要解决的 Issue 号模块名与 Issue 的模块名一致。分支名格式同样为Pull Request 类型- Issue 号例如Feature-3333。5.2 代码风格与格式化DolphinScheduler 使用Spotless统一代码风格和格式覆盖范围包括 Java 源文件、pom.xml以及 Markdown 文档。提交 Pull Request 前必须先在本地执行./mvnw spotless:apply并提交格式化结果CI 会运行./mvnw spotless:check任何文件未格式化都会导致 PR 失败。更多细节可查看 开发手册 的代码风格一栏该文档还介绍了前端pnpm run lint/pnpm run prettier的格式化方式。5.3 一个 PR 对应多个 Issue 的处理Pull Request 和 Issue 一对多的根本原因是多个 Issue 需要做大体相同的事情通常有两种解决方法把多个功能相同的 Issue 合并到同一个 Issue 上然后关闭其他 Issue多个 Issue 大体在做同一功能但存在细微差别时可以划分清楚每个 Issue 的职责将每个 Issue 的类型都标记为 Sub-Task关联到一个总 Issue 上提交 Pull Request 时每个 PR 只关联一个 Sub-Task 的 Issue。总体原则是尽量把一个 Pull Request 作为最小粒度。如果一个 PR 只做一件事贡献者容易完成影响范围也更清晰对 reviewer 的压力也会更小。六、小结E2E 测试贡献的完整路径回顾全文参与 DolphinScheduler E2E 测试贡献的完整路径为认领任务在 GitHub issue 列表搜索e2e test cases从 Project Management、Resource Center、DataSource、Security Center 四个模块中选择尚未完成的、优先级高的测试案例在 Issue 下留言认领编写用例按照 Page Object Model 在dolphinscheduler-e2e/dolphinscheduler-e2e-case中编写页面模型与测试类通过DolphinScheduler(composeFiles ...)声明测试环境用Order组织用例顺序用Awaitility AssertJ 编写断言本地验证根据环境选择-Dlocaltrue、-Dm1_chiptrue或纯 Docker 模式运行测试必要时调大等待超时并利用自动录制的 MP4 视频排查问题提交 PR按[类型-Issue号][模块名] 描述规范命名标题与分支本地执行./mvnw spotless:apply格式化后提交。整个 E2E 工程框架核心在 dolphinscheduler-e2e-core用例与页面模型在 dolphinscheduler-e2e-case均为可参考的现成实现新用例完全可以依葫芦画瓢式地扩展。【免费下载链接】dolphinschedulerApache DolphinScheduler is the modern data orchestration platform. Agile to create high performance workflow with low-code项目地址: https://gitcode.com/GitHub_Trending/dol/dolphinscheduler创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考