阅读量:5
背景介绍
博主目前要做的工作需要将一个java项目的所有RD手写的测试用例和被测方法对应起来,最后将得到的结果存入一个json文件。
本教程以项目GitHub - binance/binance-connector-java 为例。
结果展示
最终会得到一个 funcTestMap.json,里面存放着focal func(被测方法)和对应的test case.
实现原理
1. 解析被测项目的src\main目录下的源码,将每一个java源文件中的方法声明(MethodDeclaration)存进一个map,其中key是方法名,value是完整方法题。
2. 解析被测项目的src\test\java\unit目录下的测试文件,对于每一个测试文件中的方法声明,如果方法名以"test"开头,则认为是测试用例,进入下一步。
3. 解析每一个测试用例,获取该测试用例的调用的所有方法MethodCallExpr,如果该方法存在第一步所得到的map当中,则认为该方法是测试用例的被测方法,和测试用例一起存入结果json数组中。
局限性
没有考虑到overload的情况。由于是根据方法名称而不是名称+参数类型作为方法的标识符。在存入阶段,遇到重载的方法,后面进的会覆盖掉之前的;在召回阶段,仅仅使用方法名称来查找方法体,有一定的几率找到错误的被测方法。
完整代码
项目结构如下
JsonCreator.java
package org.example; import com.github.javaparser.ast.body.MethodDeclaration; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.util.Map; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.charset.StandardCharsets; public class JsonCreator { public static void createAndSaveJson1(Map<String, MethodDeclaration> map, String saveJsonPath) { JsonArray jsonArray = new JsonArray(); for (Map.Entry<String, MethodDeclaration> entry : map.entrySet()) { JsonObject keyValue = new JsonObject(); keyValue.addProperty("key", entry.getKey()); keyValue.addProperty("value", String.valueOf(entry.getValue())); jsonArray.add(keyValue); } Gson gson = new GsonBuilder().setPrettyPrinting().create(); String jsonOutput = gson.toJson(jsonArray); try { Files.write(Paths.get(saveJsonPath), jsonOutput.getBytes(StandardCharsets.UTF_8)); System.out.println("JSON file saved successfully to " + saveJsonPath); } catch (Exception e) { System.err.println("Error writing JSON to file: " + e.getMessage()); } } public static void createAndSaveJson2(Map<MethodDeclaration, MethodDeclaration> map, String saveJsonPath) { JsonArray jsonArray = new JsonArray(); for (Map.Entry<MethodDeclaration, MethodDeclaration> entry : map.entrySet()) { JsonObject keyValue = new JsonObject(); keyValue.addProperty("focal func", String.valueOf(entry.getKey())); keyValue.addProperty("test case", String.valueOf(entry.getValue())); jsonArray.add(keyValue); } Gson gson = new GsonBuilder().setPrettyPrinting().create(); String jsonOutput = gson.toJson(jsonArray); try { Files.write(Paths.get(saveJsonPath), jsonOutput.getBytes(StandardCharsets.UTF_8)); System.out.println("JSON file saved successfully to " + saveJsonPath); } catch (Exception e) { System.err.println("Error writing JSON to file: " + e.getMessage()); } } }
TestMethodAnalyzer.java
package org.example; import com.github.javaparser.ParseResult; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.comments.JavadocComment; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.utils.SourceRoot; import java.io.IOException; import java.nio.file.Paths; import java.util.HashMap; import java.util.List; import java.util.Map; public class TestMethodAnalyzer { private static final Map<String, MethodDeclaration> methodMap = new HashMap<>(); private static final Map<MethodDeclaration, MethodDeclaration> funcTestMap = new HashMap<>(); public static void main(String[] args) throws IOException { // Load and index all Java files from the main source directory SourceRoot mainSourceRoot = new SourceRoot(Paths.get("C:\\dataset\\binance-connector-java\\src\\main")); mainSourceRoot.tryToParseParallelized().forEach(TestMethodAnalyzer::indexMethodDeclarations); System.out.println("The size of the map is: " + methodMap.size()); String saveJsonPath1 = "C:\\codes\\Java\\extract_func_test_pair\\src\\main\\java\\org\\methodMap.json"; JsonCreator.createAndSaveJson1(methodMap,saveJsonPath1); // Now parse the test files SourceRoot testSourceRoot = new SourceRoot(Paths.get("C:\\dataset\\binance-connector-java\\src\\test\\java\\unit")); testSourceRoot.tryToParseParallelized().forEach(TestMethodAnalyzer::analyzeTestClass); System.out.println("The size of the map is: " + funcTestMap.size()); String saveJsonPath2 = "C:\\codes\\Java\\extract_func_test_pair\\src\\main\\java\\org\\funcTestMap.json"; JsonCreator.createAndSaveJson2(funcTestMap,saveJsonPath2); } private static void indexMethodDeclarations(ParseResult<CompilationUnit> result) { result.ifSuccessful(cu -> cu.findAll(MethodDeclaration.class).forEach(md -> { md.setJavadocComment((JavadocComment) null); methodMap.put(md.getNameAsString(), md); })); } public static void analyzeTestClass(ParseResult<CompilationUnit> result) { result.ifSuccessful(cu -> { cu.accept(new VoidVisitorAdapter<Void>() { @Override public void visit(MethodDeclaration md, Void arg) { super.visit(md, arg); if (md.getNameAsString().startsWith("test")) { analyzeTestMethods(md); } } }, null); }); } private static void analyzeTestMethods(MethodDeclaration md) { List<MethodCallExpr> methodCalls = md.findAll(MethodCallExpr.class); for (MethodCallExpr call : methodCalls) { MethodDeclaration method = methodMap.get(call.getNameAsString()); if (method != null) { System.out.println("Test Method: "); System.out.println(md); System.out.println("Calls Method body:"); method.setJavadocComment((JavadocComment)null); System.out.println(method); System.out.println("---------------------------------------"); funcTestMap.put(method,md); } } } }
maven依赖
只有两个(位于pom.xml)
<dependencies> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.10.1</version> </dependency> <dependency> <groupId>com.github.javaparser</groupId> <artifactId>javaparser-symbol-solver-core</artifactId> <version>3.26.0</version> </dependency> </dependencies>