关于Junit源码的一些探索

简介: 关于Junit源码的一些探讨

Junit介绍

Junit 是由 Erich Gamma 和 Kent Beck 编写的一个开源的单元测试框架,主要用于白盒测试。Junit 的设计精简,易学易用,但是功能却非常强大。

在Junit3中,使用时只需继承TestCase类,并且

1. 测试方法名以test开头,返回void,方法中没有参数

2. 使用方法setUp()做初始化工作,使用方法tearDown()做清理工作

3. 使用Assert类的方法来判断测试用例结果

Junit源码分析

Junit的流程图:
cc6b3277cc39866a7bc0b508b3dacd4de8699a9e
Junit的完整生命周期分为 3 个阶段:初始化阶段、运行阶段和结果打印输出阶段。

1. 初始化阶段

Junit的入口函数是TestRunner.java的main(), 它主要是创建TestRunner实例和调用TestRunner的start()方法。
    public static void main(String[] args) {
        TestRunner aTestRunner = new TestRunner();
        try {
            TestResult r = aTestRunner.start(args);
            if (!r.wasSuccessful()) {
                System.exit(FAILURE_EXIT);
            }
            System.exit(SUCCESS_EXIT);
        } catch (Exception e) {
            System.err.println(e.getMessage());
            System.exit(EXCEPTION_EXIT);
        }
    }
在start()方法中,首先对命令行进行解析(-wait 等待执行 -c 指定测试类 -m 指定测试类中的测试方法 -v 打印出Junit版本信息),然后加载所有的测试方法,最后调用doRun()方法,进入Junit的运行阶段。
 public TestResult start(String[] args) throws Exception {
        String testCase = "";
        String method = "";
        boolean wait = false;

        for (int i = 0; i < args.length; i++) {
            if (args[i].equals("-wait")) {
                wait = true;
            } else if (args[i].equals("-c")) {
                testCase = extractClassName(args[++i]);
            } else if (args[i].equals("-m")) {
                String arg = args[++i];
                int lastIndex = arg.lastIndexOf('.');
                testCase = arg.substring(0, lastIndex);
                method = arg.substring(lastIndex + 1);
            } else if (args[i].equals("-v")) {
                System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma");
            } else {
                testCase = args[i];
            }
        }
        ... ...
        try {
            ... ...
            Test suite = getTest(testCase);
            return doRun(suite, wait);
        } catch (Exception e) {
            throw new Exception("Could not create and run test suite: " + e);
        }
    }

2. Junit的运行阶段

在初始化阶段最后,代码进入doRun()方法,它会 初始化TestResult,并将PrintResult的实例fPrinter加入到TestRunner的监听列表中,最后调用TestSuite的run()方法。
    public TestResult doRun(Test suite, boolean wait) {
        TestResult result = createTestResult();
        result.addListener(fPrinter);
        long startTime = System.currentTimeMillis();
        suite.run(result);
        long endTime = System.currentTimeMillis();
        long runTime = endTime - startTime;
        fPrinter.print(result, runTime);

        pause(wait);
        return result;
    }
TestSuite中存放了TestCase列表:
private Vector<Test> fTests = new Vector<Test>(10);
运行流程图为:
6c19589af7a5f36483ff6d1df7ef2ac646be710c
TestCase的runBare()将会正式进入到测试类中:
1. 调用测试类的setUp()方法,进行测试前的初始化工作
2. 调用测试类的testXXX()方法,进行测试
3. 捕获测试中发生的异常
4. 调用测试类的tearDown()方法,进行测试后的清理工作
5. 捕获tearDown()中发生的异常
6. 抛出异常
public void runBare() throws Throwable {
        Throwable exception = null;
        setUp();
        try {
            runTest();
        } catch (Throwable running) {
            exception = running;
        } finally {
            try {
                tearDown();
            } catch (Throwable tearingDown) {
                if (exception == null) exception = tearingDown;
            }
        }
        if (exception != null) throw exception;
    }

3. 结果打印输出阶段

TestResult的runProtected()用于捕获测试用例的 异常输出,当捕获到 AssertionFailedError异常时,判断测试用例运行失败,将异常信息加入到fFailures 列表中;当捕获到Throwable异常时,判断测试用例运行失败,将异常信息加入到fErrors列表中。
    public void runProtected(final Test test, Protectable p) {
        try {
            p.protect();
        } catch (AssertionFailedError e) {
            addFailure(test, e);
        } catch (ThreadDeath e) { // don't catch ThreadDeath by accident
            throw e;
        } catch (Throwable e) {
            addError(test, e);
        }
    }

    public synchronized void addFailure(Test test, AssertionFailedError e) {
        fFailures.add(new TestFailure(test, e));
        for (TestListener each : cloneListeners()) {
            each.addFailure(test, e);
        }
    }

    public synchronized void addError(Test test, Throwable e) {
        fErrors.add(new TestFailure(test, e));
        for (TestListener each : cloneListeners()) {
            each.addError(test, e);
        }
    }
最后,TestRunner调用ResultPrinter的print()方法打印出测试用例的运行结果。
    synchronized void print(TestResult result, long runTime) {
        printHeader(runTime);
        printErrors(result);
        printFailures(result);
        printFooter(result);
    }
参考博文:分析JUnit框架源代码 https://www.ibm.com/developerworks/cn/java/j-lo-junit-src/
目录
相关文章
|
Java 测试技术 Android开发
|
设计模式 自然语言处理
|
设计模式 前端开发
|
3月前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
11天前
|
Java 程序员 测试技术
Java|让 JUnit4 测试类自动注入 logger 和被测 Service
本文介绍如何通过自定义 IDEA 的 JUnit4 Test Class 模板,实现生成测试类时自动注入 logger 和被测 Service。
18 5
|
2月前
|
SQL JavaScript 前端开发
基于Java访问Hive的JUnit5测试代码实现
根据《用Java、Python来开发Hive应用》一文,建立了使用Java、来开发Hive应用的方法,产生的代码如下
69 6
|
3月前
|
测试技术
单元测试问题之使用TestMe时利用JUnit 5的参数化测试特性如何解决
单元测试问题之使用TestMe时利用JUnit 5的参数化测试特性如何解决
42 2
|
3月前
|
测试技术
如何使用 JUnit 测试方法是否存在异常
【8月更文挑战第22天】
36 0