JavaParser抽取测试用例对应的被测方法

背景介绍

博主目前要做的工作需要将一个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

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

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)

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>
相关推荐
汽车仪器仪表相关领域3 小时前
MTX-A 模拟废气温度(EGT)计 核心特性与车载实操指南
网络·人工智能·功能测试·单元测试·汽车·可用性测试
卓码软件测评12 小时前
第三方软件课题验收测试【使用Docker容器部署LoadRunner负载生成器以实现弹性压测 】
测试工具·docker·容器·性能优化·单元测试·测试用例
Apifox.1 天前
Apifox 1 月更新|MCP 调试、测试套件、测试报告重构、网络信息查看、Hoppscotch 导入
前端·人工智能·测试工具·单元测试·团队开发
卓码软件测评1 天前
第三方移动应用测试机构:【移动应用性能测试:使用LoadRunner的Mobile Application - HTTP/HTML协议】
测试工具·ci/cd·性能优化·单元测试·测试用例
汽车仪器仪表相关领域1 天前
70A大电流+三档电压可调:Midtronics MSP-070系列电源充电器汽车ECU刷新与电池维护实战全解
人工智能·功能测试·单元测试·汽车·可用性测试
我送炭你添花2 天前
Pelco KBD300A 模拟器:18. 按依赖顺序 + 复杂度由低到高逐步推进pytest单元测试
python·单元测试·log4j·pytest
CodeCraft Studio2 天前
Parasoft是什么?Parasoft自动化测试工具与解决方案:实现规模化应用
自动化测试·测试工具·单元测试·静态测试·parasoft·嵌入式软件测试·软件安全合规
卓码软件测评2 天前
【第三方双重资质软件测试机构:测试RESTful API和SOAP Web Services:LoadRunner协议选择和脚本编写】
测试工具·ci/cd·性能优化·单元测试·测试用例·restful
别会,会就是不问2 天前
Junit4下Mockito包的使用
java·junit·单元测试
凹凸曼coding2 天前
Java业务层单元测试通用编写流程(Junit4+Mockito实战)
java·单元测试·log4j