如何用Java正则提取测试方法中STARTING与ENDING间的代码?
提取Java测试方法中指定日志间的代码实现方案
一、正则修正方案(适用于格式规整的代码)
如果你的代码格式比较统一(比如日志语句单独成行,没有嵌套的同名日志内容),可以用修正后的正则来匹配。先看典型的测试类代码示例:
public class TestDemo { private static final Logger LOGGER = LoggerFactory.getLogger(TestDemo.class); @Test public void testLogin() { LOGGER.info("STARTING TEST CASE : testLogin"); // 业务代码段 User user = new User("admin", "123456"); boolean result = userService.login(user); assertTrue(result); LOGGER.info("ENDING TEST CASE: testLogin"); } @Test public void testQuery() { LOGGER.info("STARTING TEST CASE : testQuery"); // 另一段业务代码 List<Order> orders = orderService.queryByUserId(1); assertFalse(orders.isEmpty()); LOGGER.info("ENDING TEST CASE: testQuery"); } }
之前的正则失效大概率是没处理多行匹配或用了贪婪模式,修正后的实现代码如下:
import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexCodeExtractor { public static List<String> getTestCodeSegments(String javaFileContent) { List<String> codeList = new ArrayList<>(); // 正则规则:匹配START日志到END日志之间的内容,支持多行,非贪婪匹配 String regex = "LOGGER\\.info\\(\"STARTING TEST CASE : .*?\"\\);\\s*(.*?)\\s*LOGGER\\.info\\(\"ENDING TEST CASE: .*?\"\\);"; Pattern pattern = Pattern.compile(regex, Pattern.DOTALL | Pattern.MULTILINE); Matcher matcher = pattern.matcher(javaFileContent); while (matcher.find()) { String segment = matcher.group(1).trim(); if (!segment.isEmpty()) { codeList.add(segment); } } return codeList; } }
关键注意点:
- 加
Pattern.DOTALL让.能匹配换行符,支持跨多行的代码段 - 用
.*?非贪婪匹配,避免一次性匹配多个测试方法的内容 - 要保证日志语句的格式和正则完全匹配(比如引号、空格、冒号的位置不能错)
二、Java语法解析方案(更可靠,适配复杂代码)
正则处理Java代码容易踩坑(比如嵌套方法、字符串里包含类似日志的内容、多行注释干扰),用专业的Java语法解析器(比如JavaParser)是更稳妥的选择:
- 先引入JavaParser依赖(Maven):
<dependency> <groupId>com.github.javaparser</groupId> <artifactId>javaparser-core</artifactId> <version>3.25.8</version> </dependency>
- 实现提取逻辑:
import com.github.javaparser.JavaParser; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.stmt.Statement; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class ParserCodeExtractor { public static List<String> extractTestCode(File javaTestFile) throws IOException { List<String> codeSegments = new ArrayList<>(); CompilationUnit cu = JavaParser.parse(javaTestFile); // 遍历所有方法,筛选测试方法并提取目标代码 new VoidVisitorAdapter<Void>() { @Override public void visit(MethodDeclaration method, Void arg) { super.visit(method, arg); // 只处理带@Test注解的测试方法 if (method.getAnnotationByName("Test").isPresent()) { List<Statement> statements = method.getBody().get().getStatements(); boolean isInTargetBlock = false; StringBuilder codeSb = new StringBuilder(); for (Statement stmt : statements) { // 判断是否是START/END日志语句 if (stmt.isExpressionStmt() && stmt.asExpressionStmt().getExpression().isMethodCallExpr()) { MethodCallExpr logCall = stmt.asExpressionStmt().getExpression().asMethodCallExpr(); if (logCall.getName().asString().equals("info") && logCall.getScope().isPresent() && logCall.getScope().get().asName().asString().equals("LOGGER")) { String logContent = logCall.getArguments().get(0).asStringLiteralExpr().getValue(); if (logContent.startsWith("STARTING TEST CASE :")) { isInTargetBlock = true; continue; } if (logContent.startsWith("ENDING TEST CASE:")) { isInTargetBlock = false; if (codeSb.length() > 0) { codeSegments.add(codeSb.toString().trim()); codeSb.setLength(0); } continue; } } } // 处于目标区间内时,拼接代码 if (isInTargetBlock) { codeSb.append(stmt.toString()).append("\n"); } } } } }.visit(cu, null); return codeSegments; } }
这个方案的优势:
- 精准识别Java语法结构,不会被字符串、注释里的干扰内容误导
- 可以精准过滤带@Test注解的测试方法
- 支持复杂代码结构(比如嵌套if、循环、多行代码)
三、正则失效的常见原因排查
如果之前的正则没生效,大概率是这几个问题:
- 没加
Pattern.DOTALL,导致.无法匹配换行符,跨多行的代码段匹配不到 - 用了贪婪匹配
.*,导致一次性匹配了多个测试方法的内容 - 日志语句的格式和正则不匹配(比如多了空格、引号用了单引号、冒号位置不对)
内容的提问来源于stack exchange,提问作者Psl
相关产品推荐
相关产品推荐

