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

Cucumber-jvm:如何从TestCaseFinished事件中获取嵌入式图片?

解决Cucumber插件中无法获取scenario.embed/write内容的问题

我来帮你梳理这个问题——我之前也遇到过类似的情况,Cucumber的事件模型确实有这个设计特点,咱们一步步拆解:

为什么TestCaseFinished事件不包含这些内容?

Cucumber的事件系统是职责分离的设计思路:

  • TestCaseFinished事件的核心作用是传递测试用例的整体结果元数据,比如成功/失败状态、执行时长、所属特性文件等,它并不负责携带测试过程中产生的附加输出内容。
  • scenario.embed()生成的截图、scenario.write()生成的文本,属于测试步骤的附属输出,Cucumber会通过专门的EmbedEvent和WriteEvent来单独触发这些内容的传递,不会把它们打包到TestCaseFinished里。这种设计能避免单个事件过于臃肿,让插件开发者可以按需监听特定类型的事件。

如何在插件中获取这些内容?

最优雅的方式是同时监听EmbedEvent、WriteEvent和TestCaseFinished事件,通过缓存关联测试用例和它的附加内容,具体步骤如下:

  1. 维护一个缓存(比如Map),用测试用例的唯一标识作为键,存储对应的截图/文本内容
  2. 监听EmbedEvent:每次触发时,把截图的二进制数据、媒体类型存入缓存,关联到对应的测试用例
  3. 监听WriteEvent:每次触发时,把文本内容存入缓存,关联到对应的测试用例
  4. 监听TestCaseFinished:触发时,从缓存中取出当前测试用例的所有附加内容,和测试结果一起上传到TestRail,之后清理缓存避免内存泄漏

代码示例(以Java插件为例)

public class TestRailReporter implements EventListener {
    // 用测试用例的URI+名称作为唯一标识,存储附加内容
    private final Map<String, List<Object>> testCaseAttachments = new HashMap<>();

    @Override
    public void setEventPublisher(EventPublisher publisher) {
        // 注册各类事件的处理器
        publisher.registerHandlerFor(EmbedEvent.class, this::handleEmbed);
        publisher.registerHandlerFor(WriteEvent.class, this::handleWrite);
        publisher.registerHandlerFor(TestCaseFinished.class, this::handleTestCaseFinished);
    }

    private void handleEmbed(EmbedEvent event) {
        String testCaseKey = getUniqueTestCaseKey(event.getTestCase());
        testCaseAttachments.computeIfAbsent(testCaseKey, k -> new ArrayList<>())
                .add(new ScreenshotAttachment(event.getData(), event.getMediaType()));
    }

    private void handleWrite(WriteEvent event) {
        String testCaseKey = getUniqueTestCaseKey(event.getTestCase());
        testCaseAttachments.computeIfAbsent(testCaseKey, k -> new ArrayList<>())
                .add(new TextLog(event.getText()));
    }

    private void handleTestCaseFinished(TestCaseFinished event) {
        TestCase testCase = event.getTestCase();
        String testCaseKey = getUniqueTestCaseKey(testCase);
        // 获取当前测试用例的所有附加内容
        List<Object> attachments = testCaseAttachments.getOrDefault(testCaseKey, Collections.emptyList());
        
        // 执行上传逻辑:把测试结果和附加内容一起传到TestRail
        uploadResultsToTestRail(testCase, event.getResult(), attachments);
        
        // 清理缓存,避免内存占用
        testCaseAttachments.remove(testCaseKey);
    }

    // 生成测试用例的唯一标识,避免重复
    private String getUniqueTestCaseKey(TestCase testCase) {
        return testCase.getUri() + "#" + testCase.getName();
    }

    // 辅助类:封装截图附件
    private static class ScreenshotAttachment {
        private final byte[] data;
        private final MediaType mediaType;

        public ScreenshotAttachment(byte[] data, MediaType mediaType) {
            this.data = data;
            this.mediaType = mediaType;
        }

        // getter方法
    }

    // 辅助类:封装文本日志
    private static class TextLog {
        private final String text;

        public TextLog(String text) {
            this.text = text;
        }

        // getter方法
    }

    // 自定义上传到TestRail的逻辑
    private void uploadResultsToTestRail(TestCase testCase, Result result, List<Object> attachments) {
        // 这里可以把截图转成Base64,或者直接用二进制数据上传
        // 文本日志可以作为测试结果的备注添加到TestRail中
    }
}

关于@After钩子方案的补充

你提到的用@After钩子存文件的方式确实可行,但缺点也很明显:

  • 需要自己处理测试用例(TestCase)和场景(Scenario)的对应关系(比如Scenario Outline会生成多个TestCase),容易出错
  • 磁盘IO操作会增加额外的性能开销,不如直接通过事件收集内存中的数据高效
  • 后续还要读取文件、清理文件,增加了代码复杂度

所以更推荐用多事件监听的方案,这也是Cucumber插件设计的标准用法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:43:59