经过研究junit3.8的源代码,学习了junit的测试思想。在此做成一个总结:
1.junit中测试单位分成两种:
TestCase 与 TestSuite
其中TestCase为最小的测试单位,即:每个单元测试的最小功能模块。说到这里,大家可能会想到那么我们平时写的测试类不就是一个最小测试单位吗。例如:
package onlyfun.caterpillar.test;
import onlyfun.caterpillar.MathTool;
import junit.framework.TestCase;
public class MathToolTest extends TestCase {
public MathToolTest(String testMethod) {
super(testMethod);
}
public void testGcd() {
assertEquals(5, MathTool.gcd(10, 5));
}
public static void main(String[] args) {
junit.textui.TestRunner.run(MathToolTest.class);
}
}
那么 MathToolTest这个类不就是最小的测试单位吗。这里要着重说明下,最小的测试单位是每一个方法。
具体大家可以看看junit源代码TestCase 类,TestCase类中有一个private String fName;
这个成员变量指出了此TestCase对应的method:(这里就是
public MathToolTest(String testMethod) {
super(testMethod);
}
这段代码指定方法名的用途
).
下面说说TestSuite我们测试的时候不可能一个个最小的单元测试模块TestCase这样运行吧(注意:这里TestCase指的是每一个方法)。于是TestSuite出现了。它将多个TestCase整合起来一起测试。可能你会说junit.swingui.TestRunner.run(ChartServiceImplTest.class);
我这样运行的junit没有写TestSuite啊。让我们看看源代码:
BaseTestRunner类中
synchronized public void runSuite() {
if (fRunner != null) {
fTestResult.stop();
} else {
setLoading(shouldReload());
reset();
showInfo("Load Test Case...");
final String suiteName= getSuiteText();
final Test testSuite= getTest(suiteName);
if (testSuite != null) {
addToHistory(suiteName);
doRunTest(testSuite);
}
}
}
...........................
/**
* Returns the Test corresponding to the given suite. This is
* a template method, subclasses override runFailed(), clearStatus().
*/
public Test getTest(String suiteClassName) {
if (suiteClassName.length() <= 0) {
clearStatus();
return null;
}
Class testClass= null;
try {
testClass= loadSuiteClass(suiteClassName);
} catch (ClassNotFoundException e) {
String clazz= e.getMessage();
if (clazz == null)
clazz= suiteClassName;
runFailed("Class not found \""+clazz+"\"");
return null;
} catch(Exception e) {
runFailed("Error: "+e.toString());
return null;
}
Method suiteMethod= null;
try {
suiteMethod= testClass.getMethod(SUITE_METHODNAME, new Class[0]);
} catch(Exception e) {
// try to extract a test suite automatically
clearStatus();
return new TestSuite(testClass);
}
if (! Modifier.isStatic(suiteMethod.getModifiers())) {
runFailed("Suite() method must be static");
return null;
}
Test test= null;
try {
test= (Test)suiteMethod.invoke(null, (Object[])new Class[0]); // static method
if (test == null)
return test;
}
catch (InvocationTargetException e) {
runFailed("Failed to invoke suite():" + e.getTargetException().toString());
return null;
}
catch (IllegalAccessException e) {
runFailed("Failed to invoke suite():" + e.toString());
return null;
}
clearStatus();
return test;
}
哈哈,还是没有逃脱生成TestSuite的魔爪吧。只不过可能一个是你自己写的一个是默认生成的这点区别而已。
2.好了通过上面的一个介绍,不知道大家对于junit的运行是否有了一个概要的思路。下面我们说下TestSuite的生成方式:
junit.textui.TestRunner.run(ChartServiceImplTest.class);
这样调用的方式在1中说明了,是junit框架自己生成的TestSuite对象。生成的过程如下:
首先查看该类中是否包含了public static Test suite() 方法,如果包含了,直接采用此处方法生成的Test对象(即:TestSuite)对象。如下:
public class ChartServiceImplTest extends TestCase {
public static Test suite() {
TestSuite suite= new TestSuite();
suite.addTestSuite(ChartServiceImplTest.class);
suite.addTest(new ChartServiceImplTest("testMethod"));
return suite;
}
public void testQueryChartList() {
fail("Not yet implemented");
// assertEquals(true,true);
}
public ChartServiceImplTest(String methodName){
super(methodName);
}
}
如果不包含,则采用类反射方式查询TestCase中test开头的方法,封装成一个个的Testcase,然后统一生成TestSuite.
Testcase对象中包含了一个变量private String fName;运行时将根据fName查找Testcase对象中的名称为fName的方法,然后采用类反射的方式运行该方法(即:执行test开头的测试方法)。
3.通过查看可以得出:
public static Test suite() {
TestSuite suite= new TestSuite();
suite.addTestSuite(ChartServiceImplTest.class);
suite.addTest(new ChartServiceImplTest("testMethod"));
return suite;
}
suite对象可以包含suite对象,也可以包含TestCase对象。这里大家看到了这个地方
suite.addTestSuite(ChartServiceImplTest.class);恐怕会问:怎么是suite对象呢。
我们看下源代码: /**
* Adds the tests from the given class to the suite
*/
public void addTestSuite(Class testClass) {
addTest(new TestSuite(testClass));
}
/**
* Adds a test to the suite.
*/
public void addTest(Test test) {
fTests.addElement(test);
}
大家看出来了吧,先转换为TestSuite对象的。
ok继续研究:addTest方法其实但是为Test类类型,我们查看Test类源代码:
/**
* A <em>Test</em> can be run and collect its results.
*
* @see TestResult
*/
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);
}
可以看出,TestSuite与TestCase对象都将运行public abstract void run(TestResult result)这个方法。TestResult 对象可以控制控制台或者swing界面,从而控制显示正确与错误.好了说到有点乱。
总结说就是:TestRunner将会运行TestSuite的run(TestResult result)方法。
1.而这个TestSuite本身内部有包含了TestSuite与TestCase对象,所以:
2.这个TestSuite的run(TestResult result)实现中:
运行其包含的TestCase的run(TestResult result)方法
运行其包含的TestSuite的run(TestResult result)方法(这个回到步骤1(源代码实现就是如此
))
4.junit运行方式:
junit.textui.TestRunner.run(ChartServiceImplTest.class);
junit.awtui.TestRunner.run(ChartServiceImplTest.class);
junit.swingui.TestRunner.run(ChartServiceImplTest.class);
大家不妨看啊看看 junit.swingui.TestRunner源代码,如果swing学得好的话,自己也能写出一个junit测试展示。(eclipse的junit插件不就是如此吗
)
分享到:
评论