You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Ant实现Cucumber文件并行执行(无需Maven/TestNG)

实现方案:Ant + Java多线程实现Cucumber并行处理Excel数据

完全可以不用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>

关键注意事项

  1. 依赖准备:把Cucumber(cucumber-java、cucumber-core)、Apache POI(poi、poi-ooxml)、以及主frame应用相关的依赖jar包都放到lib目录下。
  2. 线程安全:如果主frame应用是GUI程序,确保每个线程启动独立的应用实例,避免资源冲突。
  3. 灵活性:可以修改ExcelSplitter的拆分逻辑,比如按总行数自动平均分配,而不是固定拆分5条。

内容的提问来源于stack exchange,提问作者amar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 22:30:53