您的位置:軟件測試 > 開源軟件測試 > 開源單元測試工具 > junit
簡單易懂, JUnit 框架問答
作者:網(wǎng)絡(luò)轉(zhuǎn)載 發(fā)布時(shí)間:[ 2015/7/20 14:15:45 ] 推薦標(biāo)簽:單元測試 軟件測試

  本文算是一個(gè)關(guān)于Junit4相關(guān)的知識(shí)分享,但是不同于網(wǎng)上大段的源碼分析,模式學(xué)習(xí)文章,我想通過問答的形式,引出代碼來簡明闡述JUnit4是如何實(shí)現(xiàn)需要的功能的。
  考慮到任何一個(gè)框架,都是為了解決問題而存在的。那么我想,帶著問題去看源碼會(huì)不會(huì)事半功倍呢?
  Note:本文基于Junit4.11源碼
  Junit4怎么能跑Case?
  眾所周知,JUnit框架能幫助跑單元測試的Case,那么它到底是如何實(shí)現(xiàn)的呢?換句話說,如果沒有JUnit,我們會(huì)怎么執(zhí)行Case?
  OK,很簡單,一個(gè)case是一個(gè)方法,那要想執(zhí)行他們,直接在Main方法里調(diào)用可以了。
  但是當(dāng)Case很多時(shí),我們得一個(gè)一個(gè)在Main方法里顯示的調(diào)用Case。
  雖然稍顯繁瑣,還是可以解決問題的,不過拓展性不敢恭維了,別人也沒辦法使用我們已寫的東西?!
  那么,如果要上升到框架層面,能夠讓更多的人使用,該怎么做呢? Junit給了我們很好的例子.
  好的單元測試框架一定是大限度的方便使用者,讓其盡可能只關(guān)注單元測試本身,而不是其他一些冗余的事情.
  很明顯Junit做到了這一點(diǎn), 它不需要你去顯示的自己調(diào)用Case,一切都幫你做好,你只要告訴它要測哪個(gè)類好了,以JUnit4.11默認(rèn)的Runner為例:
  BlockJUnit4ClassRunner aRunner = new BlockJUnit4ClassRunner(JunitTest.class);
  aRunner.run(new RunNotifier());
  Debug進(jìn)去,我們會(huì)發(fā)現(xiàn),原來JUnit是用反射機(jī)制來跑我們寫的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是實(shí)際執(zhí)行方法的類,跟進(jìn)去方法:fTestMethod.invokeExplosively(fTarget);
  我們會(huì)看到Method.invoke(obj,args)的終反射調(diào)用。
  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();
  }
  如何實(shí)現(xiàn) @Test 標(biāo)記功能?
  Junit在4.0以后進(jìn)行了一個(gè)大的版本變動(dòng),引入了Java 1.5的注解功能,像我們常用的 @Test, @BeforeClass, @AfterClass 一樣,那么這些功能是如何實(shí)現(xiàn)的呢?
  以 @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標(biāo)記, 表示這個(gè)注解在運(yùn)行時(shí)仍然會(huì)被JVM保存,因此可以通過反射得到它:
  protected List<FrameworkMethod> computeTestMethods() {
  return getTestClass().getAnnotatedMethods(Test.class);
  }
  這樣JUnit得到了所有你要跑的Case。
  而對于 @BeforeClass, @AfterClass 都是一個(gè)原理。
  Junit4如何判斷結(jié)果?
  上面說到如何找Case,如何跑Case,那么對于一個(gè)完成的執(zhí)行流程,我們還缺一塊:你的Case是Pass還是Fail?
  比如下面:
  org.junit.Assert.assertTrue("check assert method", false);
  我們通常是用Assert去做斷言的,還有其他的方法,比如assertEqulas,assertNotEqulas。顯而易見,上面的斷言結(jié)果一定是Fail的,那具體是如何實(shí)現(xiàn)的呢?
  要解開謎題,我們先看看Assert.assertTrue(String message, boolean condition)方法的具體實(shí)現(xiàn):
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);
}
  當(dāng)condition是false時(shí),它會(huì)拋一個(gè)java.lang.AssertionError異常。這個(gè)類是從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();
}
}
  這個(gè)方法目的是執(zhí)行單個(gè)Case,它是用Try Catch來捕獲所有非預(yù)期的異常錯(cuò)誤。
  只要Catch到Throwable異常了,很明顯Case掛了。反之,Case則是pass的。
  這里還可以繼續(xù)深入,比如我們知道,@Test 有一個(gè)expected參數(shù),可以讓我們填入期望捕獲的異常,那么JUnit如何實(shí)現(xiàn)的呢?
  總結(jié)
  好了,我們來看看我們都說了啥:
  Junit通過 @Test 注解,找到我們想跑的Case
  通過反射,來執(zhí)行我們的Case
  后通過Catch Throwable exception來判斷我們的case 是Pass還是Fail
  很明顯JUnit還有很多其他的功能,比如TestSuite,Rule,各種Runners,這里不在一一羅列了。
  我想,作為表達(dá)一條Case執(zhí)行的主線,講到這里應(yīng)該已經(jīng)足夠了,不是嘛?

軟件測試工具 | 聯(lián)系我們 | 投訴建議 | 誠聘英才 | 申請使用列表 | 網(wǎng)站地圖
滬ICP備07036474 2003-2017 版權(quán)所有 上海澤眾軟件科技有限公司 Shanghai ZeZhong Software Co.,Ltd