Junit源码阅读心得(1)
来源:互联网 发布:淘宝店铺怎么装修上新 编辑:程序博客网 时间:2024/05/29 19:45
大家好,这些天在一位前辈的建议下,开始阅读Junit源码,当然这个过程中,也有在参考他人的经验,有兴趣的朋友可以去看一下下面这位前辈的博文,相信可以给各位一些收获:JUNIT源码分析,下面和大家分享一些博主自己在阅读源码时的浅显心得,希望各位多多指教,不当之处,还望不吝赐教。
首先我们来依次说明几个Junit中的核心的类,分别为以下几个类和接口:
public interface Testpublic abstract class TestCase extends Assert implements Testpublic 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 runnersprivate 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。
好了,暂时就和大家分享这些,如有不足之处,还请多多指教。
- Junit源码阅读心得(1)
- JUnit源码阅读 -- 阅读基础
- zepto源码阅读心得与过程1
- java源码阅读心得
- mybatis源码阅读心得
- ArrayBlockingQueue源码阅读心得
- ReentrantLock源码阅读心得
- JUnit源码阅读 -- 测试入口
- Memcache源码阅读(1)---看源码的心得
- linux源码,list_entry阅读心得
- libevent源码学习-----阅读心得
- JUNIT源码阅读(二)– org.junit.*
- 阅读>心得(1)
- junit心得
- 基于Android的Gson源码阅读心得
- JUNIT4.11源码阅读(一)--org.junit.Assert类
- Struts源码阅读心得之logic:notPresent篇
- ExecutorService.invokeAny()和ExecutorService.invokeAll()的源码阅读心得
- css如何让两个并列的按钮 水平居中显示
- canvas系列教程05-柱状图项目3
- springboot 基于 maven 自定义格式打包
- gradle编译: Plugin with id 'java-library' not found.
- SQL Server 数据库规范
- Junit源码阅读心得(1)
- 单链表找交点
- 炫酷简单的loading效果
- canvas系列教程06-柱状图项目4
- (A5,一)java反射机制
- eclipse运行断点时找不到类 source not found
- linux系统 安装配置jdk tomcat mysql
- myImage负责往页面中添加img标签
- sql 修改字段中部分值