本文算是一個關於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/] 若是您對本文有意見或者建議,歡迎留言