如何用TestNG记录测试历史并生成Java API测试失败统计报告?
简单实现方案:基于JUnit 5扩展(推荐)
这是当前Java生态最常用的测试方案,不用额外折腾工具,直接在IntelliJ里就能实现你要的重复执行+失败统计需求:
步骤1:给测试方法添加重复执行注解
用JUnit 5的@RepeatedTest(100)让目标测试自动跑100次,方法级注解可以灵活控制每个测试的执行次数:
import org.junit.jupiter.api.RepeatedTest; import static org.junit.jupiter.api.Assertions.assertEquals; public class MyApiTests { @RepeatedTest(100) void myTest() { // 替换成你的API测试逻辑:发送请求、断言响应 int actualStatusCode = callApi(); assertEquals(200, actualStatusCode); } // 模拟API请求(实际项目替换为真实调用) private int callApi() { double random = Math.random(); if (random < 0.25) return 304; if (random < 0.30) return 404; return 200; } }
步骤2:自定义失败统计扩展
写一个JUnit 5扩展类,监听测试失败事件,自动收集每个测试的失败原因、次数和堆栈信息:
import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.TestExecutionExceptionHandler; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; public class FailureStatisticsExtension implements TestExecutionExceptionHandler { // 存储统计数据:键为「测试方法名+失败原因」组合,值为失败详情 private static final Map<String, FailureRecord> failureRecords = new HashMap<>(); @Override public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { String testName = context.getRequiredTestMethod().getName(); String failureReason = throwable.getMessage(); String stackTrace = getStackTraceAsString(throwable); // 生成唯一键,确保同一测试的不同失败原因分开统计 String key = testName + "|" + failureReason; failureRecords.compute(key, (k, record) -> { if (record == null) { return new FailureRecord(testName, failureReason, 1, stackTrace); } else { record.incrementCount(); return record; } }); // 继续抛出异常,不影响测试的失败状态 throw throwable; } // 将异常堆栈转为字符串 private String getStackTraceAsString(Throwable throwable) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); throwable.printStackTrace(pw); return sw.toString(); } // 测试全部完成后生成指定格式的报告 public static void generateReport() { System.out.println("\n=== 测试失败统计报告 ==="); for (FailureRecord record : failureRecords.values()) { System.out.printf("failingTest: %s, failureReason: %s, timesOccurred: %d/100, stackTrace: %s%n", record.testName, record.failureReason, record.count, record.stackTrace); } } // 内部类存储单条失败记录 private static class FailureRecord { String testName; String failureReason; int count; String stackTrace; public FailureRecord(String testName, String failureReason, int count, String stackTrace) { this.testName = testName; this.failureReason = failureReason; this.count = count; this.stackTrace = stackTrace; } public void incrementCount() { this.count++; } } }
步骤3:注册扩展并生成报告
在测试类上注册扩展,并用@AfterAll触发报告生成:
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.extension.ExtendWith; import static org.junit.jupiter.api.Assertions.assertEquals; @ExtendWith(FailureStatisticsExtension.class) public class MyApiTests { @RepeatedTest(100) void myTest() { int actualStatusCode = callApi(); assertEquals(200, actualStatusCode); } private int callApi() { double random = Math.random(); if (random < 0.25) return 304; if (random < 0.30) return 404; return 200; } @AfterAll static void generateFailureReport() { FailureStatisticsExtension.generateReport(); } }
备选方案:基于TestNG
如果你项目用的是TestNG,也可以用类似逻辑实现:
import org.testng.ITestResult; import org.testng.annotations.AfterSuite; import org.testng.annotations.Test; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; public class MyApiTestNGTests { private static final Map<String, FailureRecord> failureRecords = new HashMap<>(); @Test(invocationCount = 100) void myTest() { int actualStatusCode = callApi(); assert actualStatusCode == 200 : "Expected 200 but got " + actualStatusCode; } private int callApi() { double random = Math.random(); if (random < 0.25) return 304; if (random < 0.30) return 404; return 200; } // 监听测试失败事件 public void onTestFailure(ITestResult result) { String testName = result.getName(); Throwable throwable = result.getThrowable(); String failureReason = throwable.getMessage(); String stackTrace = getStackTraceAsString(throwable); String key = testName + "|" + failureReason; failureRecords.compute(key, (k, record) -> { if (record == null) { return new FailureRecord(testName, failureReason, 1, stackTrace); } else { record.incrementCount(); return record; } }); } private String getStackTraceAsString(Throwable throwable) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); throwable.printStackTrace(pw); return sw.toString(); } @AfterSuite void generateReport() { System.out.println("\n=== 测试失败统计报告 ==="); for (FailureRecord record : failureRecords.values()) { System.out.printf("failingTest: %s, failureReason: %s, timesOccurred: %d/100, stackTrace: %s%n", record.testName, record.failureReason, record.count, record.stackTrace); } } private static class FailureRecord { String testName; String failureReason; int count; String stackTrace; public FailureRecord(String testName, String failureReason, int count, String stackTrace) { this.testName = testName; this.failureReason = failureReason; this.count = count; this.stackTrace = stackTrace; } public void incrementCount() { this.count++; } } }
运行测试后,控制台会直接输出你要求格式的统计报告。如果需要把报告写入文件,只需要把System.out.println替换为文件写入逻辑即可。
内容的提问来源于stack exchange,提问作者MasterJoe
相关产品推荐
相关产品推荐

