Anlr and StringTemplate instances: automatically generate unit test classes,

Source: Internet
Author: User
Tags lexer

Anlr and StringTemplate instances: automatically generate unit test classes,
Anlr and StringTemplate instances: automatically generated unit test Class 1. anlr syntax

To automatically generate a unit test, the first step is to analyze the tested class. Here we use Java code as an example to analyze Java code with anlr. It is hard to imagine the complexity of writing anlr syntax files in a language by hand. It is very considerate to list the syntax files in many common languages on anlr's GitHub website, such as Java, SQL Syntax of Sqlite and MySQL.

With the. g4 syntax file, you can automatically generate the parser code by following the steps in the anlr v4 getting started tutorial and instance.

2. StringTemplate Basics

StringTemplate (ST for short) is also a very useful tool provided by anlr. Its functions are similar to template engines such as Velocity and FreeMaker. You can customize templates to render different webpages, emails, and codes based on different values. But we can see from the String in the name that it is relatively lightweight. This is true after a trial. It supports hard-coded simple templates in Java.

However, I still encountered a lot of problems during the trial. I always felt that its template syntax was somewhat complicated! In addition, the latest ST4 API and previous versions have greatly changed, and many examples found on the Internet are not easy to use. For details, refer to the tutorials on the official website and the CheatSheet table. Of course, it takes a lot of time to debug the sample code in this article!

3. Unit Test Generator

First, let's look at the Main method. The input text is a Java class represented by the code variable, which has two methods. Then, use the automatically generated anlr code to construct the processing chain of the semantic analyzer and parser, and pass inUnitTestGeneratorThe listener traverses the input text.

public class JavaCodeParseTest {    public static void main(String[] args) {        String code =                "package com.jcache.store;" +                "public class CacheStore {" +                    "Object getCache(int a) {" +                        "if (a == 1)" +                            "return 1;" +                        "else " +                            "return 2;" +                    "}" +                    "void setCache(int a) {" +                        "return;" +                    "}" +                "}";        // 1.Lexical analysis        JavaLexer lexer = new JavaLexer(new ANTLRInputStream(code));        CommonTokenStream tokens = new CommonTokenStream(lexer);        // 2.Syntax analysis        JavaParser parser = new JavaParser(tokens);        ParseTree tree = parser.compilationUnit();        // 3.Application based on Syntax Tree        ParseTreeWalker walker = new ParseTreeWalker();        walker.walk(new UnitTestGenerator(), tree);    }}

Here is a core codeUnitTestGenerator.
Here are some key points in the Code:

  • ST template group: it is simple to define a template in a template file. If you want to define a template in Java, you must refer to the static initialization block method in this example.
  • Nested sub-templates and Multi-valued: to automatically generate a method for each method name based on the list of method names, follow
/** * Simple unit test generator. */public class UnitTestGenerator extends JavaBaseListener {    /** Constants: template name, placeholder name, generated code name */    private static final String CLASS_ST_NAME = "classST";    private static final String METHOD_ST_NAME = "methodST";    private static final String TEST_PKG_NAME = "testPkgName";    private static final String TEST_CLASS_NAME = "testClassName";    private static final String TEST_METHOD_NAME = "testMethodName";    private static final String CLASS_NAME_SUFFIX = "Test";    private static final String METHOD_NAME_PREFIX = "test";    /** Template for Java */    private static final String METHOD_ST =            t("@Test") +            t("public void " + $(TEST_METHOD_NAME) + "() throws Exception {") +            tt("//body...") +            t("}") +            n("");    private static final String CLASS_ST =            n("package " + $(TEST_PKG_NAME) + ";") +            n("") +            n("import org.junit.*;") +            n("") +            n("public class " + $(TEST_CLASS_NAME) + " {") +            n("") +            /**             * Apply nested template 'methodST' to multi-valued attributes 'testMethodName'.             * NOTE: <attribute:template(argument-list)>             *      Apply template to attribute with optional argument-list.             *      Example: <name:bold()> applies bold() to name's value.             *      The first argument of the template gets the iterated value.             */            n($(TEST_METHOD_NAME + ":" + METHOD_ST_NAME +"();separator=\"\n\"")) +            n("}");    /** ST group to nest template */    private static STGroup group;    static {        group = new STGroup('$', '$');        CompiledST classST = group.defineTemplate(CLASS_ST_NAME, CLASS_ST);        classST.addArg(new FormalArgument(TEST_PKG_NAME));        classST.addArg(new FormalArgument(TEST_CLASS_NAME));        classST.addArg(new FormalArgument(TEST_METHOD_NAME));        CompiledST methodST = group.defineTemplate(METHOD_ST_NAME, METHOD_ST);        methodST.addArg(new FormalArgument(TEST_METHOD_NAME));    }    /** Attributes. NOTE: group.getInstanceOf() return new ST */    private Map<String, Object> attributeMap = new HashMap<>();    @Override    public void enterPackageDeclaration(@NotNull JavaParser.PackageDeclarationContext ctx) {        attributeMap.put(TEST_PKG_NAME, ctx.qualifiedName().getText());    }    @Override    public void enterClassDeclaration(@NotNull ClassDeclarationContext ctx) {        String orgClassName = ctx.Identifier().getText();        String testClassName = orgClassName + CLASS_NAME_SUFFIX;        attributeMap.put(TEST_CLASS_NAME, testClassName);    }    @Override    public void enterMethodDeclaration(@NotNull MethodDeclarationContext ctx) {        String orgMethodName = ctx.Identifier().getText();        String testMethodName = METHOD_NAME_PREFIX + orgMethodName.substring(0, 1).toUpperCase()                + orgMethodName.substring(1);        // Multi-valued attribute        List<String> methodNames = (List<String>) attributeMap.get(TEST_METHOD_NAME);        if (methodNames == null) {            methodNames = new ArrayList<>();            attributeMap.put(TEST_METHOD_NAME, methodNames);        }        methodNames.add(testMethodName);    }    @Override    public void enterStatement(@NotNull StatementContext ctx) {        // If/else/switch, for/while, try-catch        switch (ctx.getStart().getText()) {            case "if":                break;            case "for":                break;            case "try":                break;            default:                break;        }    }    @Override    public void exitCompilationUnit(@NotNull JavaParser.CompilationUnitContext ctx) {        ST template = group.getInstanceOf(CLASS_ST_NAME);        attributeMap.entrySet().forEach(e -> template.add(e.getKey(), e.getValue()));        System.out.println(template.render());        // Cleanup        attributeMap.clear();    }    // =======================================    //          String Utility    // =======================================    private static String $(String attrName) {        return "$" + attrName + "$";    }    private static String n(String str) {        return str + "\n";    }    private static String t(String str) {        return "\t" + n(str);    }    private static String tt(String str) {        return "\t\t" + n(str);    }}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.