写点什么

Junit4 遇上 chatGPT

  • 2024-06-06
    北京
  • 本文字数:5207 字

    阅读完需:约 17 分钟

这是一篇适合 Java 工程师体质的 AI 开发教程。


本教程会教你写一个简单的 junit4 的 Rule,该 Rule 在基于 junit4 的测试方法失败后,自动向 GPT 发送错误信息并通过 GPT 分析得出代码修改建议。

首先向 AI 问好

简单的通过 AI,让它基于 json 格式生成相应的类文件,这样方便我们在代码中使用,而不用显示写 json 字符串。


package com.jd.jr.parrot.llm;
import lombok.Data;
/** * created on 2023/7/25 20:07 * * @author Dragonchu */@Datapublic class Choice { private String finish_reason; private int index; private Message message;}
复制代码


package com.jd.jr.parrot.llm;
import lombok.Data;
/** * created on 2023/7/25 20:07 * * @author Dragonchu */@Datapublic class Message { private String role; private String content;}
复制代码


package com.jd.jr.parrot.llm;
import lombok.Data;
/** * created on 2023/7/25 20:06 * * @author Dragonchu */@Datapublic class Usage { private int completion_tokens; private int prompt_tokens; private int total_tokens;}
复制代码


package com.jd.jr.parrot.llm;
import java.util.List;import lombok.Data;
/** * created on 2023/7/25 20:17 * * @author Dragonchu */@Datapublic class OpenAiRequest { private String erp; private List<Message> messages; private String model;}
复制代码


package com.jd.jr.parrot.llm;
import java.util.List;import lombok.Data;
/** * created on 2023/7/25 20:04 * * @author Dragonchu */@Datapublic class OpenAiResponse { private long created; private Usage usage; private String model; private String id; private List<Choice> choices; private String object;}
复制代码


封装一个简单的工具,以供使用 GPT


package com.jd.jr.parrot.llm;
import com.google.gson.Gson;import java.util.Arrays;import lombok.extern.slf4j.Slf4j;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpPost;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.util.EntityUtils;
import java.io.IOException;import java.nio.charset.Charset;
/** * created on 2023/7/25 20:07 * * @author Dragonchu */@Slf4jpublic class LLMUtil { private static String url = "Your url"; private static String apiKey = "Your key";
public static String sendMsg(String msg){ OpenAiRequest request = new OpenAiRequest(); Message message = new Message(); message.setRole("user"); message.setContent(msg); request.setMessages(Arrays.asList(message)); request.setModel("gpt-3.5-turbo"); Gson gson = new Gson(); String result = postGPT(url, gson.toJson(request)); OpenAiResponse openAiResponse = gson.fromJson(result, OpenAiResponse.class); return openAiResponse.getChoices().get(0).getMessage().getContent(); }
/** * 发送 post 请求 * * @param url 请求地址 * @param jsonStr Form表单json字符串 * @return 请求结果 */ public static String postGPT(String url, String jsonStr) { // 创建httpClient CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建post请求方式实例 HttpPost httpPost = new HttpPost(url); httpPost.setHeader("Content-type", "application/json;charset=utf-8"); httpPost.setHeader("Authorization", apiKey);
// 设置参数---设置消息实体 也就是携带的数据 StringEntity entity = new StringEntity(jsonStr, Charset.forName("UTF-8")); // 设置编码格式 entity.setContentEncoding("UTF-8"); // 发送Json格式的数据请求 entity.setContentType("application/json"); // 把请求消息实体塞进去 httpPost.setEntity(entity); // 执行http的post请求 CloseableHttpResponse httpResponse; String result = null; try { httpResponse = httpClient.execute(httpPost); result = EntityUtils.toString(httpResponse.getEntity(), "UTF-8"); if (httpResponse.getStatusLine().getStatusCode() != 200) { System.out.println("post->return error,result: " + result); log.error("post->return error,result: {}", result); } } catch (IOException e) { log.error("postStream->error", e); e.printStackTrace(); } return result; }
public static void main(String[] args) { log.info(sendMsg("Who are you?")); }
}
复制代码


然后写一个 Watcher

junit4 提供了 Rule 机制,该机制利用 AOP,使得我们可以在测试代码中嵌入相应的逻辑,比如在执行代码前输出方法名,捕获异常等。TestWatcher 是一个抽象类,通过覆盖该类我们可以得到一个满足我们需求的 Rule。


我们希望能在测试方法失败抛出异常后,直接将异常信息和异常堆栈发送给 GPT,同时,也要将出错的代码文件发送给 GPT(注意敏感信息哦,本文只是一个教程,请勿私自将敏感代码发送给 GPT)。


简单写一个工具类,这个类封装了从异常堆栈中获取文件的能力。


package com.jd.jr.parrot;
import java.io.BufferedReader;import java.io.File;import java.io.FileReader;import java.io.IOException;import java.nio.file.Path;import java.nio.file.Paths;import java.util.regex.Matcher;import java.util.regex.Pattern;
/** * created on 2023/7/28 15:08 * * @author Dragonchu */public class ExceptionUtil { public static String getFileName(String stackTraceLine) { // 使用正则表达式提取文件名 Pattern pattern = Pattern.compile("\((.*\.java)"); Matcher matcher = pattern.matcher(stackTraceLine); if (matcher.find()) { String fileName = matcher.group(1); return fileName; } return null; }
public static Path getPath(String stackTraceLine) {
// 提取文件路径 int firstParenthesisIndex = stackTraceLine.indexOf("("); int firstDotIndex = stackTraceLine.lastIndexOf(".", firstParenthesisIndex); String packagePath = stackTraceLine.substring(0, firstDotIndex).replace(".", File.separator); // 获取根路径 String rootPath = new File("").getAbsolutePath();
Path filePath = Paths.get(rootPath, "src", "main", "java", packagePath + ".java"); return filePath; }
public static String getCodeFile(String stackTraceLine) { Path filePath = getPath(stackTraceLine); if (filePath.toFile().exists()) { // 读取文件内容 try (BufferedReader reader = new BufferedReader(new FileReader(filePath.toFile()))) { StringBuilder content = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { content.append(line).append("\n"); } return content.toString(); } catch (IOException e) { e.printStackTrace(); } } return null; }
public static void main(String[] args) { String stackTraceLine = "com.jd.jr.parrot.ExceptionExample.main(ExceptionExample.java:24)"; String fileName = getFileName(stackTraceLine); System.out.println("fileName = " + fileName); String path = getPath(stackTraceLine).toString(); System.out.println("path = " + path); String codeFile = getCodeFile(stackTraceLine); System.out.println("codeFile = " + codeFile); }}
复制代码


简单地设计一个 prompt,也就是我们需要发给 GPT 的信息。


private static final String prompt = ""You are a good Java engineer, please analyze the following error and provide possible solutions." +    "The error message is : %s, The error stack is : %s. The code file is : %s";
复制代码


当测试方法抛出异常后,我们将异常相关信息以及代码文件发送给 GPT 让它进行分析。


package com.jd.jr.parrot.junit;
import com.jd.jr.parrot.ExceptionUtil;import com.jd.jr.parrot.llm.LLMUtil;import java.util.Arrays;import lombok.extern.slf4j.Slf4j;import org.junit.rules.TestWatcher;import org.junit.runner.Description;
/** * created on 2023/7/28 14:07 * * @author Dragonchu */@Slf4jpublic class OverWatch extends TestWatcher {
private static final String prompt = ""You are a good Java engineer, please analyze the following error and provide possible solutions." + "The error message is : %s, The error stack is : %s. The code file is : %s";
@Override protected void failed(Throwable e, Description description) { super.failed(e, description); String codeFile = ExceptionUtil.getCodeFile(e.getStackTrace()[0].toString()); log.info(LLMUtil.sendMsg(String.format(prompt, e.getMessage(), Arrays.toString(e.getStackTrace()),codeFile))); }}
复制代码

让 AI 给我一份错误代码

本着能不自己干就不自己干的态度,我们让 AI 生成一份有错误的代码。


这是我与 AI 的对话:


我:


给我一份会抛出异常的代码,我希望这份代码稍微复杂一点,且依赖了一些外部包,它错误的原因比较难以排查。


AI:


以下是一个稍微复杂一点且依赖外部包的会抛出异常的 Java 代码示例:


📎importorg.jsoup.Jsoup;importorg.jsoup.nodes.Document;importorg.jsoup.nodes.Element;importorg.jsoup.select.Elements;importjava.io.IOException;publicclassExceptionExample{    publicstaticvoidmain(String[]args){        try{            // 使用Jsoup抓取网页内容            Documentdoc=Jsoup.connect("https://www.example.com").get();                        // 解析网页内容            Elementtitle=doc.select("title").first();            System.out.println("Title: "+title.text());                        // 尝试获取一个不存在的元素            ElementnonExistentElement=doc.select("#nonexistent-element").first();                        // 在非空元素上调用方法,会抛出NullPointerException            nonExistentElement.text();                    }catch(IOExceptione){            e.printStackTrace();        }    }}
复制代码


这段代码使用外部包 Jsoup 来抓取网页内容并解析,然后尝试获取一个不存在的元素并在其上调用方法,会抛出 NullPointerException 异常。该异常可能比较难以排查,需要检查代码是否正确处理了异常情况,以及是否正确使用外部包的 API 等。

测试,让 AI 在 junit 测试失败后分析代码

简单写一个测试:


package com.jd.jr.parrot;
import com.jd.jr.parrot.junit.OverWatch;import java.io.IOException;import org.junit.FixMethodOrder;import org.junit.Rule;import org.junit.Test;import org.junit.runners.MethodSorters;
/** * created on 2023/7/28 14:14 * * @author Dragonchu */@FixMethodOrder(MethodSorters.NAME_ASCENDING)public class OverWatchTest { @Rule public OverWatch overWatch = new OverWatch();
@Test public void jsoupTest() throws IOException { ExceptionExample.main(new String[]{}); }}
复制代码


测试结果



作者:京东科技 储惠龙


来源:京东云开发者社区

发布于: 刚刚阅读数: 4
用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
Junit4遇上chatGPT_京东科技开发者_InfoQ写作社区