本文算是一个关于Junit4相关的知识分享,可是不一样于网上大段的源码分析,模式学习文章,我想经过问答的形式,引出代码来简明阐述JUnit4是如何实现须要的功能的。
考虑到任何一个框架,都是为了解决问题而存在的。那么我想,带着问题去看源码会不会事半功倍呢?
Note:本文基于Junit4.11源码java
众所周知,JUnit框架能帮助跑单元测试的Case,那么它究竟是如何实现的呢?换句话说,若是没有JUnit,咱们会怎么执行Case?
OK,很简单,一个case就是一个方法,那要想执行他们,直接在Main方法里调用就能够了。
可是当Case不少时,咱们就得一个一个在Main方法里显示的调用Case。
虽然稍显繁琐,仍是能够解决问题的,不过拓展性就不敢恭维了,别人也没办法使用咱们已写的东西?!框架
那么,若是要上升到框架层面,可以让更多的人使用,该怎么作呢? Junit给了咱们很好的例子.ide
好的单元测试框架必定是最大限度的方便使用者,让其尽量只关注单元测试自己,而不是其余一些冗余的事情.
很明显Junit作到了这一点, 它不须要你去显示的本身调用Case,一切都帮你作好,你只要告诉它要测哪一个类就行了,以JUnit4.11默认的Runner为例:源码分析
BlockJUnit4ClassRunner aRunner = new BlockJUnit4ClassRunner(JunitTest.class); aRunner.run(new RunNotifier());
Debug进去,咱们就会发现,原来JUnit是用反射机制来跑咱们写的Case。单元测试
public class InvokeMethod extends Statement { private final FrameworkMethod fTestMethod; private Object fTarget; public InvokeMethod(FrameworkMethod testMethod, Object target) { fTestMethod = testMethod; fTarget = target; } @Override public void evaluate() throws Throwable { fTestMethod.invokeExplosively(fTarget); } }
这里的InvokeMethod就是实际执行方法的类,跟进去方法:fTestMethod.invokeExplosively(fTarget);
咱们就会看到Method.invoke(obj,args)的最终反射调用。学习
public Object invokeExplosively(final Object target, final Object... params) throws Throwable { return new ReflectiveCallable() { @Override protected Object runReflectiveCall() throws Throwable { return fMethod.invoke(target, params); } }.run(); }
Junit在4.0之后进行了一个大的版本变更,引入了Java 1.5的注解功能,就像咱们经常使用的 @Test, @BeforeClass, @AfterClass 同样,那么这些功能是如何实现的呢?
以 @Test 为例:测试
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Test { static class None extends Throwable { private static final long serialVersionUID = 1L; private None() { } } Class<? extends Throwable> expected() default None.class; long timeout() default 0L; }
它以RetentionPolicy.RUNTIME
标记, 就表示这个注解在运行时仍然会被JVM保存,所以就能够经过反射获得它:ui
protected List<FrameworkMethod> computeTestMethods() { return getTestClass().getAnnotatedMethods(Test.class); }
这样JUnit就获得了全部你要跑的Case。lua
而对于 @BeforeClass, @AfterClass 都是一个原理。code
上面说到如何找Case,如何跑Case,那么对于一个完成的执行流程,咱们还缺一块:你的Case是Pass仍是Fail?
好比下面:
org.junit.Assert.assertTrue("check assert method", false);
咱们一般是用Assert去作断言的,还有其余的方法,好比assertEqulas,assertNotEqulas。显而易见,上面的断言结果必定是Fail的,那具体是如何实现的呢?
要解开谜题,咱们先看看Assert.assertTrue(String message, boolean condition)
方法的具体实现:
static public void assertTrue(String message, boolean condition) { if (!condition) { fail(message); } } static public void fail(String message) { if (message == null) { throw new AssertionError(); } throw new AssertionError(message); }
当condition是false时,它会抛一个java.lang.AssertionError
异常。这个类是从Java 1.4引入的,而且它跟全部的Java的异常同样都是从Throwable
继承来的。
那Junit如何捕获呢?
在org.junit.runners.ParentRunner<T>
类咱们找到了答案:
protected final void runLeaf(Statement statement, Description description, RunNotifier notifier) { EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description); eachNotifier.fireTestStarted(); try { statement.evaluate(); } catch (AssumptionViolatedException e) { eachNotifier.addFailedAssumption(e); } catch (Throwable e) { eachNotifier.addFailure(e); } finally { eachNotifier.fireTestFinished(); } }
这个方法目的就是执行单个Case,它是用Try Catch
来捕获全部非预期的异常错误。
只要Catch到Throwable异常了,很明显Case就挂了。反之,Case则是pass的。
这里还能够继续深刻,好比咱们知道,@Test 有一个
expected
参数,可让咱们填入指望捕获的异常,那么JUnit如何实现的呢?
好了,咱们来看看咱们都说了啥:
很明显JUnit还有不少其余的功能,好比TestSuite,Rule,各类Runners,这里不在一一罗列了。
我想,做为表达一条Case执行的主线,讲到这里应该已经足够了,不是嘛?
若是您看了本篇博客,以为对您有所收获,请点击下面的 [推荐]
若是您想转载本博客,请注明出处大卡的博客[http://www.cnblogs.com/jinsdu/] 若是您对本文有意见或者建议,欢迎留言