Junit源码阅读心得(1)

大家好,这些天在一位前辈的建议下,开始阅读Junit源码,当然这个过程中,也有在参考他人的经验,有兴趣的朋友可以去看一下下面这位前辈的博文,相信可以给各位一些收获:JUNIT源码分析,下面和大家分享一些博主自己在阅读源码时的浅显心得,希望各位多多指教,不当之处,还望不吝赐教。
首先我们来依次说明几个Junit中的核心的类,分别为以下几个类和接口:

public interface Test

public abstract class TestCase extends Assert implements Test

public class TestSuite implements Test

以下是三个类的继承关系:

这里写图片描述
可以看到Test是作为接口存在,然后TestSuite与TestCase分别实现了Test接口,其中有两个方法:

public interface Test {
    /**
     * Counts the number of test cases that will be run by this test.
     */
    public abstract int countTestCases();

    /**
     * Runs a test and collects its result in a TestResult instance.
     */
    public abstract void run(TestResult result);
}

其中方法countTestCases()可以获取到TestCase的数量,而run()方法则是运行Case,这里的TestCase可以简单理解为一个测试用例。
然后我们可以看到TestCase类还实现了Assert类,这样就可以获得一点优势,就是Assert类中的方法对于Case来说是完全透明的,使用TestCase的地方可以直接调用这些方法。
那这样的继承关系有什么好处呢?
首先我们来解释一下,这种继承关系在设计模式中叫做组合模式,TestSuite中可以组合多个TestCase用于测试:

public TestSuite(Class<? extends TestCase>[] classes, String name) {
        this(classes);
        setName(name);
    }

同样我们也可以通过添加Test实例组合TestSuite:

// Cannot convert this to List because it is used directly by some test runners
private Vector<Test> fTests= new Vector<Test>(10); 
public void addTest(Test test) {
        fTests.add(test);
    }

也就是说只要实现了Test接口的实现类,都可以组合到TestSuite中,当然,这其中也包括TestSuite自身,这样就形成了一个树状形式,在运行测试时,就可以逐层进行。
然后我们再来介绍一个类:

public class TestResult extends Object

TestResult中封装了测试运行结果,并作为返回结果,返回给Client,我们看一下其属性:

//测试失败
    protected List<TestFailure> fFailures;
//测试错误
    protected List<TestFailure> fErrors;
//监听器
    protected List<TestListener> fListeners;

上面我们介绍了封装测试用例的TestCase和TestSuite以及其接口Test,并且介绍了封装测试结果的TestResult,那么有了结果,有了被执行对象,还缺少的就是测试执行者:

public class TestRunner extends BaseTestRunner

TestRunner就是测试执行者,我们在执行单元测试时,往往看到这样的注解:

@RunWith(SpringJUnit4ClassRunner.class)

这里就是指定测试执行的Runner,当然我们也可以不指定,那么默认的Runner就是:

public class BlockJUnit4ClassRunner extends ParentRunner<FrameworkMethod>

好了,我们接下来看一下TestRunner是怎么执行Test的:

static public TestResult run(Test test) {
        TestRunner runner= new TestRunner();
        return runner.doRun(test);
}

public TestResult doRun(Test test) {
        return doRun(test, false);
}

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;
}

我们可以看到首先创建了TestRunner,然后使用Runner运行Test,首先创建了记录测试结果的TestResult对象,并向result中注册监听者,然后调用Test的run()方法进行测试,这个过程中涉及到另外的一个设计模式:观察者模式:
这里写图片描述
在这里我们向result中注册了我们的监听器,也就是对于Runner来说,扮演了观察者的身份,而result扮演了被观察者的身份,当result的状态变化时,可以通过观察媒介:监听器对Runner进行通知,或者说Runner通过监听器,监听到了result的变化,从而做出响应。
那么在这里的每个Test是怎么运行的呢?由于实现Test接口的类为TestSuite和TestCase,所以我们来看一下这两个类:
在TestSuite中:

public void run(TestResult result) {
        for (Test each : fTests) {
            if (result.shouldStop() )
                break;
            runTest(each, result);
        }
    }

我们可以看到,在TestSuite中依次调用了各个测试用例的封装来进行测试,也就是之前写到的树状结构,如果each为TestCase则调用TestCase,如果为TestSuite,则调用TestSuite,这样依次调用,直至全部调用完成。
接下来看一下TestCase:

/**
     * Runs the test case and collects the results in TestResult.
     */
    public void run(TestResult result) {
        result.run(this);
    }
    /**
     * Runs the bare test sequence.
     * @throws Throwable if any exception is thrown
     */
    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;
    }

这里涉及到TestResult中的run方法:

/**
* Runs a TestCase.
*/
    protected void run(final TestCase test) {
        startTest(test);
        Protectable p= new Protectable() {
            public void protect() throws Throwable {
                test.runBare();
            }
        };
        runProtected(test, p);

        endTest(test);
    }

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);
        }
    }

我们看到在TestCase中,调用TestResult的run()方法,在TestResult中,我们注册了回调方法,也就是TestResult最终还是回调回了TestCase中的runBare()方法,同时我们看到在TestResult中的runProtected方法中,对各种类型的Throwable进行了捕获,也就是说当发生Throwable时,就会把对应的Throwable添加进我们前面说的三个List属性中。
而在TestCase中,真正执行Test的是runTest()方法:

protected void runTest() throws Throwable {
        assertNotNull("TestCase.fName cannot be null", fName); // Some VMs crash when calling getMethod(null,null);
        Method runMethod= null;
        try {
            // use getMethod to get all public inherited
            // methods. getDeclaredMethods returns all
            // methods of this class but excludes the
            // inherited ones.
            runMethod= getClass().getMethod(fName, (Class[])null);
        } catch (NoSuchMethodException e) {
            fail("Method \""+fName+"\" not found");
        }
        if (!Modifier.isPublic(runMethod.getModifiers())) {
            fail("Method \""+fName+"\" should be public");
        }

        try {
            runMethod.invoke(this);
        }
        catch (InvocationTargetException e) {
            e.fillInStackTrace();
            throw e.getTargetException();
        }
        catch (IllegalAccessException e) {
            e.fillInStackTrace();
            throw e;
        }
    }

在这里运用反射,根据存储的方法名称:fName找到要执行的方法,并予以执行,执行完毕后由TestResult做后续处理:endTest()方法:

public void endTest(Test test) {
        for (TestListener each : cloneListeners())
            each.endTest(test);
}

在这里我们遍历了所有的监听器,并告知执行已完毕。然后TestCase返回TestResult。
好了,暂时就和大家分享这些,如有不足之处,还请多多指教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值