在本地 idea 中自动生成测试用例

在上篇文章中,我们已经生成了刷题列表。如下图所示:

图片说明

详见:https://www.nowcoder.com/discuss/1009527

下面,我们将牛客题目中自带的测试用例,进行自动生成。方便更愉快的刷题、以及补充新的测试用例。

如果题目中自带多个示例,那么我们也就同时生成多个测试用例。

准备工作

为了生成测试用例,我们需要将牛客网题库自带的题目示例和方法体拷贝到前面生成的Java 类和 markdown 中。
具体做法如下:
(1)拷贝方法体到 Java 类中。
图片说明
如下所示:
图片说明

(3)将题目示例拷贝到 markdown 中,这里推荐使用 Typora 这个 markdown 编辑器,非常好用。
图片说明

下面,我们要做的工作就是,解析 markdown 中示例的输入和返回值。然后自动生成测试用例。

生成测试参数

解析方法体

通过解析Java类中的方法体,确定好方法名称、参数类型、参数名称、返回值等信息。
以便我们后续调用。

    public static ProblemInfo getJavaInfo(QuestionInfo questionInfo) {
        try {
            File file = new File(CreateJavaFileUtil.getJavaPath(questionInfo));
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(
                            new FileInputStream(file)));
            String linestr;
            while ((linestr = br.readLine()) != null) {
                if (!linestr.contains("class") && linestr.contains("public")) {
                    Matcher matcher = pattern.matcher(linestr);
                    if (matcher.find()) {
                        ProblemInfo info = new ProblemInfo();
                        info.setQuestionInfo(questionInfo);
                        info.setProblemName(questionInfo.getQuestionTitle());
                        info.setClassName(questionInfo.getJavaName());
                        info.setReturnType(matcher.group(1).trim());
                        info.setMethodName(matcher.group(2).trim());
                        String[] strings = matcher.group(3).split(",");
                        for (int i = 0; i < strings.length; i++) {
                            info.addParam(strings[i].trim());
                        }
                        return info;
                    }
                }
            }
            br.close();//关闭IO
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("请检查" + questionInfo.getJavaName() + "类中是否有初始化方法!!");
        return null;
    }

解析 markdown

通过解析Markdown ,解析好测试用例的相关参数,存储在上面的对象中。

    public static void getMarkDown(ProblemInfo info) {
        try {
            String filePath = CreateJavaFileUtil.getMarkPath(info.getQuestionInfo());
            File file = new File(filePath);
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(
                            new FileInputStream(file)));
            String linestr, key = "", value = "";//按行读取 将每次读取一行的结果赋值给linestr
            boolean inStart = false;
            boolean inEnd = false;
            while ((linestr = br.readLine()) != null) {
                if (linestr.contains("```") || linestr.contains("说明") || linestr.contains("解析") || linestr.contains("示例")) {
                    inEnd = false;
                }
                if (!inEnd && value != "") {
                    if (key.endsWith(",")) {
                        key = key.substring(0, key.length() - 1);
                    }
                    info.addTest(key.replaceAll("`", ""), value);
                    key = "";
                    value = "";
                }
                if (linestr.contains("返回值:")) {
                    br.readLine();
                    br.readLine();
                    linestr = br.readLine();
                    inStart = false;
                    inEnd = true;
                    value += linestr.trim();
                }
                if (linestr.contains("输入:") || inStart) {
                    linestr = br.readLine();
                    key += linestr.trim();
                    inStart = true;
                }
            }
            br.close();//关闭IO
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

生成测试类

下面就可以进行组装,生成测试类了。

    public static void createTestFile(ProblemInfo info) {
        try {
            File file = new File(getTestPath(info.getQuestionInfo()));
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            if (file.exists()) {
                System.out.println(info.getClassName() + "Test.java is exists.");
                return;
            }
            FileOutputStream out = new FileOutputStream(file, true);
            StringBuffer sb = new StringBuffer();
            sb.append("package com.jueee.nowcoder." + CreateJavaFileUtil.CODE_TYPE + ";\n\n");
            sb.append("import org.junit.jupiter.api.Test;\n\n");
            for (String imports : info.getImportList()) {
                sb.append(imports + "\n");
            }
            sb.append("import static org.junit.jupiter.api.Assertions.*;\n\n");
            sb.append(CreateJavaFileUtil.getClassAnnotation(info.getQuestionInfo()));
            sb.append("public class " + info.getClassName() + "Test {\n\n");
            sb.append("\t" + info.getClassName() + " solution = new " + info.getClassName() + "();\n\n");
            int num = 1;
            for (String test : info.getTestMap().keySet()) {
                sb.append("\t@Test\n");
                sb.append("\tpublic void test" + num++ + "() {\n");
                for (String paramInfo : info.getTestParamInfo(test)) {
                    sb.append("\t\t" + paramInfo + ";\n");
                }
                for (String assertInfo : info.getAssertInfo(test)) {
                    sb.append("\t\t" + assertInfo + "\n");
                }
                sb.append("\t}\n");
            }
            sb.append("\n\n}");
            out.write(sb.toString().getBytes("utf-8"));
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

生成断言

针对不同的返回类型,生成不同的断言。

    public List<String> getAssertInfo(String in) {
        String out = getTestMap().get(in);
        List<String> list = new ArrayList<>();
        if (Arrays.asList("void").contains(getReturnType())) {
            list.add("solution." + getMethodName() + "(" + getParamName() + ");");
            String type = getParamType().get(0);
            list.add(getAssertType(type) + "(" + getParamList().get(0).replace(type, "").trim() + ", " + getObject(type, out) + ");");
        } else if (Arrays.asList("ListNode").contains(getReturnType())) {
            list.add("ListNode actual = " + getObject(getReturnType(), out) + ";");
            list.add("ListNodeUtil.print(actual);");
            list.add("ListNode expected = solution." + getMethodName() + "(" + getParamName() + ");");
            list.add("ListNodeUtil.print(expected);");
            list.add("assertTrue(ListNodeUtil.isSameListNode(expected, actual));");
        } else if (Arrays.asList("TreeNode").contains(getReturnType())) {
            list.add("TreeNode actual = " + getObject(getReturnType(), out) + ";");
            list.add("TreeNodeShow.show(actual);");
            list.add("TreeNode expected = solution." + getMethodName() + "(" + getParamName() + ");");
            list.add("TreeNodeShow.show(expected);");
            list.add("assertTrue(TreeNodeUtil.isSameTree(expected, actual));");
        } else if (getReturnType().contains("List<TreeNode>")) {
            list.add("List<TreeNode> expected = solution." + getMethodName() + "(" + getParamName() + ");");
            list.add("List<TreeNode> actual = " + getObject(getReturnType(), out) + ";");
            list.add("AssertArrayPlus.assertListTreeNode(expected, actual);");
        } else if (Arrays.asList("TreeLinkNode").contains(getReturnType())) {
            list.add("TreeLinkNode actual = " + getObject(getReturnType(), out) + ";");
            list.add("TreeLinkNodeShow.show(actual);");
            list.add("TreeLinkNode expected = solution." + getMethodName() + "(" + getParamName() + ");");
            list.add("TreeLinkNodeShow.show(expected);");
            list.add("assertTrue(TreeLinkNodeUtil.isSameTree(expected, actual));");
        } else if (Arrays.asList("ArrayList<ArrayList<Integer>>").contains(getReturnType())) {
            list.add("ArrayList<ArrayList<Integer>> actual = new ArrayList<>();");
            String[] infos = out.split("\\],\\[");
            for (int i = 0; i < infos.length; i++) {
                list.add("ArrayList<Integer> list"+i+" = new ArrayList<>();");
                String tt = infos[i].replaceAll("\\[","").replaceAll("\\]","");
                if (StringUtils.isNotBlank(tt)) {
                    String[] nums = tt.split(",");
                    for (String num : nums) {
                        list.add("list" + i + ".add(" + num + ");");
                    }
                }
                list.add("actual.add(list"+i+");");
            }
            list.add("assertEquals(solution." + getMethodName() + "(" + getParamName() + ").toString(), actual.toString());");
        } else if (getReturnType().contains("List<")) {
            list.add(getAssertCommon("solution." + getMethodName() + "(" + getParamName() + ")", getObject(getReturnType(), out) + ""));
        } else if (getReturnType().contains("boolean")) {
            list.add(getAssertDefault(getObject(getReturnType(), out).toLowerCase()));
        } else {
            list.add(getAssertDefault(getObject(getReturnType(), out)));
        }
        return list;
    }

效果展示

最终,我们即可得到如下的测试用例:
图片说明

对于链表,可以生成如下的测试用例:
https://www.nowcoder.com/practice/f9f78ca89ad643c99701a7142bd59f5d
图片说明

对于复杂的二叉树,我们也可以生成复杂的测试用例:
https://www.nowcoder.com/practice/a9d0ecbacef9410ca97463e4a5c83be7
图片说明

目前不足

(一)对于自定义类,由于情况比较复杂,所以生成的测试用例比较难判断,再次不进行考虑。好在这种情况也并不多。
(2)对于多个参数的情况,需要在markdown中手动补充下参数名称,方便分隔判断。
https://www.nowcoder.com/practice/6e5207314b5241fb83f2329e89fdecc8 这个例子。
需要补充改造如下所示:
图片说明
那么即可生成测试用例如下所示:
图片说明

#网易##Java##笔试##刷题##题库#
全部评论
刷刷列表,这个好啊
点赞 回复 分享
发布于 2022-08-12 21:13

相关推荐

产品经理傅立叶:1.建议把个人信息码一下 2.简历的排版还得优化一下,看上去有点乱,另外有一个实习经历实习时间好像多写了一个; 3.实习产出要量化
点赞 评论 收藏
分享
冲芭芭拉鸭:你这图还挺新,偷了。
投递美团等公司9个岗位
点赞 评论 收藏
分享
评论
1
1
分享
牛客网
牛客企业服务