如何基于Ant实现Cucumber文件并行执行(无需Maven/TestNG)
完全可以不用Maven插件或TestNG,通过Java多线程结合Ant构建来实现你的需求。核心思路是先拆分Excel数据,再用多线程分别启动Cucumber运行时处理不同的数据分片,最后用Ant完成编译和执行。
步骤1:拆分Excel数据
先写一个Java工具类,把abc.xlsx拆分成两个各含5条数据的子文件(abc_part1.xlsx和abc_part2.xlsx),用Apache POI处理Excel操作:
import org.apache.poi.ss.usermodel.*; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class ExcelSplitter { public static void splitExcel(String sourcePath) throws IOException { Workbook sourceWorkbook = WorkbookFactory.create(new FileInputStream(sourcePath)); Sheet sourceSheet = sourceWorkbook.getSheetAt(0); // 创建两个子工作簿 Workbook part1Workbook = WorkbookFactory.create(true); Sheet part1Sheet = part1Workbook.createSheet("Data"); Workbook part2Workbook = WorkbookFactory.create(true); Sheet part2Sheet = part2Workbook.createSheet("Data"); // 复制表头 Row header = sourceSheet.getRow(0); copyRow(header, part1Sheet.createRow(0)); copyRow(header, part2Sheet.createRow(0)); // 拆分数据行 int rowCount = sourceSheet.getPhysicalNumberOfRows(); for (int i = 1; i < rowCount; i++) { Row sourceRow = sourceSheet.getRow(i); if (i <= 5) { // 前5条数据写入part1 copyRow(sourceRow, part1Sheet.createRow(i)); } else { // 后5条数据写入part2 copyRow(sourceRow, part2Sheet.createRow(i - 5)); } } // 保存子文件 part1Workbook.write(new FileOutputStream("abc_part1.xlsx")); part2Workbook.write(new FileOutputStream("abc_part2.xlsx")); // 关闭资源 sourceWorkbook.close(); part1Workbook.close(); part2Workbook.close(); } private static void copyRow(Row sourceRow, Row targetRow) { for (int j = 0; j < sourceRow.getPhysicalNumberOfCells(); j++) { Cell sourceCell = sourceRow.getCell(j); Cell targetCell = targetRow.createCell(j); if (sourceCell != null) { // 根据实际数据类型调整,这里示例用字符串类型 targetCell.setCellValue(sourceCell.getStringCellValue()); } } } public static void main(String[] args) throws IOException { splitExcel("abc.xlsx"); } }
步骤2:调整Cucumber场景和步骤定义
修改Feature文件
复制原有场景,分别指定不同的Excel子文件,并添加标签用于筛选执行:
Scenario: Process excel part 1
@ProcessPart1
Given Launch mainframe application with "Host" "Port"
Given Login credentials with "UserId" "Pwd"
Then I want to read excel data from "abc_part1.xlsx" and process
Then I logout
Scenario: Process excel part 2
@ProcessPart2
Given Launch mainframe application with "Host" "Port"
Given Login credentials with "UserId" "Pwd"
Then I want to read excel data from "abc_part2.xlsx" and process
Then I logout
修改步骤定义
让Excel文件路径支持参数传入,替换原有的固定路径:
@Then("I want to read excel data from {string} and process") public void readAndProcessExcel(String excelFile) throws IOException { // 这里编写读取指定Excel文件并处理数据的逻辑 // 替换原有的固定读取abc.xlsx的代码 }
步骤3:Java多线程启动Cucumber并行执行
编写一个主类,先执行Excel拆分,再启动两个线程分别执行不同标签的Cucumber场景:
import io.cucumber.core.cli.Main; public class ParallelCucumberRunner { public static void main(String[] args) throws InterruptedException { // 先执行Excel拆分 try { ExcelSplitter.splitExcel("abc.xlsx"); } catch (Exception e) { e.printStackTrace(); return; } // 线程1:执行@ProcessPart1标签的场景 Thread thread1 = new Thread(() -> { String[] part1Args = { "--glue", "com.yourcompany.stepdefs", // 替换为你的步骤定义包路径 "--features", "src/test/resources/features/your_feature_file.feature", // 替换为你的feature文件路径 "--tags", "@ProcessPart1" }; Main.run(part1Args, Thread.currentThread().getContextClassLoader()); }); // 线程2:执行@ProcessPart2标签的场景 Thread thread2 = new Thread(() -> { String[] part2Args = { "--glue", "com.yourcompany.stepdefs", "--features", "src/test/resources/features/your_feature_file.feature", "--tags", "@ProcessPart2" }; Main.run(part2Args, Thread.currentThread().getContextClassLoader()); }); // 启动并等待线程执行完成 thread1.start(); thread2.start(); thread1.join(); thread2.join(); } }
步骤4:Ant构建配置
编写build.xml,配置编译、依赖和执行任务:
<project name="CucumberParallelTest" default="run-parallel" basedir="."> <!-- 依赖jar包存放目录 --> <path id="project.classpath"> <fileset dir="lib"> <include name="*.jar"/> <!-- 包含Cucumber、Apache POI、Selenium等所有依赖 --> </fileset> <pathelement location="bin"/> <!-- 编译后的类文件目录 --> </path> <!-- 编译Java源代码 --> <target name="compile"> <mkdir dir="bin"/> <javac srcdir="src" destdir="bin" classpathref="project.classpath" includeantruntime="false"/> </target> <!-- 执行并行测试 --> <target name="run-parallel" depends="compile"> <java classname="ParallelCucumberRunner" classpathref="project.classpath" fork="true"/> </target> <!-- 清理生成文件 --> <target name="clean"> <delete dir="bin"/> <delete file="abc_part1.xlsx"/> <delete file="abc_part2.xlsx"/> </target> </project>
关键注意事项
- 依赖准备:把Cucumber(
cucumber-java、cucumber-core)、Apache POI(poi、poi-ooxml)、以及主frame应用相关的依赖jar包都放到lib目录下。 - 线程安全:如果主frame应用是GUI程序,确保每个线程启动独立的应用实例,避免资源冲突。
- 灵活性:可以修改
ExcelSplitter的拆分逻辑,比如按总行数自动平均分配,而不是固定拆分5条。
内容的提问来源于stack exchange,提问作者amar

