Cucumber-jvm:如何从TestCaseFinished事件中获取嵌入式图片?
解决Cucumber插件中无法获取scenario.embed/write内容的问题
我来帮你梳理这个问题——我之前也遇到过类似的情况,Cucumber的事件模型确实有这个设计特点,咱们一步步拆解:
为什么TestCaseFinished事件不包含这些内容?
Cucumber的事件系统是职责分离的设计思路:
TestCaseFinished事件的核心作用是传递测试用例的整体结果元数据,比如成功/失败状态、执行时长、所属特性文件等,它并不负责携带测试过程中产生的附加输出内容。scenario.embed()生成的截图、scenario.write()生成的文本,属于测试步骤的附属输出,Cucumber会通过专门的EmbedEvent和WriteEvent来单独触发这些内容的传递,不会把它们打包到TestCaseFinished里。这种设计能避免单个事件过于臃肿,让插件开发者可以按需监听特定类型的事件。
如何在插件中获取这些内容?
最优雅的方式是同时监听EmbedEvent、WriteEvent和TestCaseFinished事件,通过缓存关联测试用例和它的附加内容,具体步骤如下:
- 维护一个缓存(比如Map),用测试用例的唯一标识作为键,存储对应的截图/文本内容
- 监听
EmbedEvent:每次触发时,把截图的二进制数据、媒体类型存入缓存,关联到对应的测试用例 - 监听
WriteEvent:每次触发时,把文本内容存入缓存,关联到对应的测试用例 - 监听
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
相关产品推荐
相关产品推荐

