浅读JUnit源码
为了学习下设计模式在实际项目中的应用,我决定找个开源项目来研究下,JUnit代码量少,又是GOF的亲生儿子,当然成了最佳选择。我阅读的是JUnit3.8.2的代码,相对于JUnit4,JUnit3的代码量更小,结构更清楚。
准备工作及JUnit基本用法
下载JUnit源代码,在Eclipse中新建项目,并导入下载的JUnit代码。下载地址为:http://grepcode.com/snapshot/repo1.maven.org/maven2/junit/junit/3.8.2
定义被测试代码。这里的被测试代码很简单,就是计算两个整数之和。
package my.test; public class SimpleClass { public static int add(int a, int b) { return a + b; } }
接下来写测试用例。JUnit测试用例可以通过继承junit.framework.TestCase类来实现。一个TestCase子类中每个以test开头的公共方法都是都被JUnit解析为一条TestCase,多条TestCase可以组成一个TestSuite,这里用到了Composite模式,这个后面再详细分析。JUnit4里也可以通过注解的方式(@Test)定义测试用例,这是后话。
package my.test; import junit.framework.Assert; import junit.framework.TestCase; public class TestSimpleClass extends TestCase { public void testAdd() { int result = SimpleClass.add(1, 1); Assert.assertEquals(2, result); } }
在上面的代码中,testAdd()方法会被解析成一个TestCase。也可以通过定义suite()方法来定义测试用例,两者差别不大。
准备好了被测代码和测试用例,就可以在Eclipse里设置断点、跟踪代码了。在Eclipse里launch debug的方法为,右键点击要debug的文件,debug as->debug configurations->Arguments,设置要测试的类,如图:

我是从junit.textui.TestRunner开始debug的。
JUnit结构
先看看JUnit的包结构:

JUnit的核心代码在junit.framework这个包中,junit.runner是运行测试用例时的相关代码,其中junit.runner.BaseTestRunner是一个抽象类。junit.awtui、junit.swingui、junit.textui是三种不同的运行JUnit的图形界面方式,每个包中都有一个TestRunner集成自junit.runner.BaseTestRunner。
我阅读的代码主要就是junit.framewokr、junit.textui这两个包,和junit.runner.BaseTestRunner。
下面是junit里几个重要的类的类图:

JUnit运行流程
JUnit的运行过程可以分为准备测试用例、执行用例并搜集测试结果两个阶段。以下代码中的getTest(testcase)是准备测试用例,doRun(suite, wait)执行用例:
try { if (!method.equals("")) return runSingleMethod(testCase, method, wait); Test suite= getTest(testCase); return doRun(suite, wait); } catch (Exception e) { throw new Exception("Could not create and run test suite: " + e); }
下面是时序图:

JUnit用到的设计模式
Composite
JUnit在创建测试用例时,用到了composite模式。Junit.framework.Test是定义了测试用例的接口,其中包括run()和countTestCases()两个方法。TestCase和TestSuite是实现了Test接口的两个类,TestCase是单个测试用例,TestSuite是测试用例的集合。
Observer
JUnit在搜集测试结果是用到了observer模式。TestResult管理着一个TestListener的集合,当测试用例执行出现错误或者执行失败时,通知这些TestListener执行相应操作。下面是截取的一些代码:
管理TestListener的集合:
/** * Registers a TestListener */ public synchronized void addListener(TestListener listener) { fListeners.addElement(listener); } /** * Unregisters a TestListener */ public synchronized void removeListener(TestListener listener) { fListeners.removeElement(listener); }
当测试用例执行失败时,通知TestListenner执行相关操作:
public synchronized void addError(Test test, Throwable t) { fErrors.addElement(new TestFailure(test, t)); for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) { ((TestListener)e.nextElement()).addError(test, t); } }
Template Method
Template模式比较简单,JUnit在BaseTestRunner.getTest()、TestCase.runBare()中都有用到。
浙公网安备 33010602011771号