在本地 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

相关推荐

去年九月,正值秋招,刚好学校组织多场线下招聘会经验不足,就拿着简历过去了(实际双非院校的招聘会惨不仍睹,不是销售就是教培,去的必要性不是很大)不过,九月初那场正好碰见一家国企研究所,总部在广州增城区,做质量检测的。看了一下岗位需求属于科研助理工程师,需要有一定的论文专利撰写能力,待遇也还不错,年包20左右,后两年每年以30%的幅度上涨。说实话,还是挺符合我的规划的,本身自己发表的论文还算多,同时质量检测和我本身想找的测试岗也类似,也是国央企编制。因此,便提交了简历,在一周不到便收到了面试。面试第一轮是主任面,面试感觉还可以,主要聊了很多科研方面的事情,对我的成果也很感兴趣。面试完十分钟便收到了HR的信息,通知可以约下一轮面试了,属于副总面(还在心想,果然是国央企啊,一个面试还能见到副总),不过这时候比较有意思的来了,HR和我说必须得线下面,因此让我去广州,不过可以报销车费啥的,但是具体啥时候面试又得看副总的时间…感觉有点儿扯,武汉去广州高铁得六个多小时,但是谨记不放过任何一个面试机会的态度,我还是同意了到了中午,HR又和我发消息,说可以今天就面,不过好的一点,可以线上面试,然后约好面试时间,我就忐忑地在等待着了,然而等到了面试时间,我进入面试间,等好一会儿,还是没等着副总,最终询问HR才说刚刚副总有别的会议无法面试。好的,我可以理解,毕竟是副总嘛,人家事儿多,然后又约着第二天面试。第二天到了约好的时间,四点钟开始,我很早就在面试间了,等待半小时没有任何消息,询问HR让我继续等待,继续等待一个半小时还是没消息,又让我继续等待(马上快六点了,我实习公司都快要下班了),然后再次等待半小时(总共在面试间等待两个半小时,中间还不能退出,需要一直戴着耳机听到里面的动静,副总有没有进会议),大概六点半才开始面试。骚的地方来了,我等待了两个半小时的面试,副总进来后一句不好意思都没有,直接就开始面试,总共面试就15分钟,中间还接了5分钟的电话,然后一直打断我问我为什么没在简历上面写我发表的论文名(主要是大多数公司确实不看论文啊),两个问题问就五分钟,然后让我反问五分钟就结束了,逆天的面试体验。其实更骚的一点是,他们还要手写一份应聘承诺书和个人简历表,各种奇葩的问题还需要手写(什么性格优缺点,喜欢的学科,爱好特长,为何能胜任岗位,岗位理解,英语水平等等)它家真算得上秋招面试中最奇葩的,最终挂的原因大概率是因为本身我的学历确实不太优秀,就没得到副总的看重。而在他家面试面试前一个小时,海康一面的时候,同样被质疑到:&amp;quot;你一个双非硕士,为什么能有三段实习,在我之前面试的92的同学中还有很多都没有实习的,你觉得这是什么原因?我看你拿了很多竞赛的奖项,是真的假的?方面给我看一下获奖证明吗?&amp;quot;当天正在公司,也恰巧正值学院计算综测统计竞赛获奖的情况,刚好把获奖截图发给班长,最终真的就是让我一张一张找出来给她看……我可以接受你不认可我的能力,觉得我的能力不能胜任岗位,但是接受不了这种不尊重和质疑……
国家一级产品交付工程...:这个世界也太颠了 想起来第一次实习面试 一家小公司 刚到就要填各种各样的表 然后老板来面试 一直问数学怎么样 最后还说给我开50一天 够我一天的吃饭和路费了 希望我第二天就可以来 还说给我这么好的一个学习机会 应该珍惜感谢他
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务