您的位置:首页 > 其它

JUnit 4.8 源码解读

2016-07-02 17:43 543 查看

JUnit 4.8 源码解读

首先从junit-team/junit4下载源码。

JUnit的源码分成了2个大的package,junit和org.junit。junit包实现了基础的功能。其中junit目录是JUnit 3.X的内容,org.junit是后来JUnit 4.X新加的。先来讲讲junit。

程序员在写完TestCase后,右击Run As JUnit后,IDE会从TestCase的RunWith注解决定使用哪个Runner来运行TestCase,默认是TestRunner。

public class TestRunner extends BaseTestRunner {
...
public static void main(String[] args) {
TestRunner aTestRunner = new TestRunner();
try {
TestResult r = aTestRunner.start(args);
if (!r.wasSuccessful()) {
System.exit(FAILURE_EXIT);
}
System.exit(SUCCESS_EXIT);
} catch (Exception e) {
System.err.println(e.getMessage());
System.exit(EXCEPTION_EXIT);
}
}
}


在main方法中,先新建一个TestRunner。这里会将系统输出作为参数传给构造器,生成一个ResultPrinter用于显示测试结果。

public TestRunner() {
this(System.out);
}


然后执行

TestResult r = aTestRunner.start(args);


public TestResult start(String[] args) throws Exception {
String testCase = "";
String method = "";
boolean wait = false;

for (int i = 0; i < args.length; i++) {
if (args[i].equals("-wait")) {
wait = true;
} else if (args[i].equals("-c")) {
testCase = extractClassName(args[++i]);
} else if (args[i].equals("-m")) {
String arg = args[++i];
int lastIndex = arg.lastIndexOf('.');
testCase = arg.substring(0, lastIndex);
method = arg.substring(lastIndex + 1);
} else if (args[i].equals("-v")) {
System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma");
} else {
testCase = args[i];
}
}

...
}


start方法读取了命令参数。-wait是会等用户输入,-c是指示TestCase的全类名,-m指定了要跑的方法,-v用来输出当前JUnit的版本,或者直接跟一个类名。

testCase = args[i];


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);
}


获取到方法名和类名后,如果有方法,则调用runSingleMethod(testCase, method, wait),否则调用getTest(testCase);

public Test getTest(String suiteClassName) {
if (suiteClassName.length() <= 0) {
clearStatus();
return null;
}
Class<?> testClass = null;
try {
// Class.forName(suiteClassName);
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 {
//获取 suite 方法
suiteMethod = testClass.getMethod(SUITE_METHODNAME);
} catch (Exception e) {
// try to extract a test suite automatically
clearStatus();
// 如果没有suite方法,就生成一个TestSuite
return new TestSuite(testClass);
}
// suite方法必须是static
if (!Modifier.isStatic(suiteMethod.getModifiers())) {
runFailed("Suite() method must be static");
return null;
}
Test test = null;
try {
// 调用suite方法,返回方法返回值
test = (Test) suiteMethod.invoke(null); // 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;
}


通常情况下,我们不会去实现suite方法,所以这里讲新建TestSuite。

初始化TestSuite

public TestSuite(final Class<?> theClass) {
addTestsFromTestCase(theClass);
}

private void addTestsFromTestCase(final Class<?> theClass) {
fName = theClass.getName();
try {
// 先尝试获取带一个String参数的constructor,如果没有,则获取无参constructor
getTestConstructor(theClass); // Avoid generating multiple error messages
} catch (NoSuchMethodException e) {
addTest(warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()"));
return;
}

// TestCase类必须是public的
if (!Modifier.isPublic(theClass.getModifiers())) {
addTest(warning("Class " + theClass.getName() + " is not public"));
return;
}

// 嵌套获取TestCase类的测试方法
Class<?> superClass = theClass;
List<String> names = new ArrayList<String>();
// 判断父类是否继承了Test接口
while (Test.class.isAssignableFrom(superClass)) {
for (Method each : MethodSorter.getDeclaredMethods(superClass)) {
//
addTestMethod(each, names, theClass);
}
superClass = superClass.getSuperclass();
}
if (fTests.size() == 0) {
// 如果没有满足的test method,则生成一个fail的测试用例
addTest(warning("No tests found in " + theClass.getName()));
}
}


private void addTestMethod(Method m, List<String> names, Class<?> theClass) {
String name = m.getName();
// 只添加最新的同名方法
if (names.contains(name)) {
return;
}
// isTestMethod(m) && Modifier.isPublic(m.getModifiers());
// 公有方法,且是test method
if (!isPublicTestMethod(m)) {
// 必须满足三个条件:方法无参,方法名以test开头,没有返回值
if (isTestMethod(m)) {
addTest(warning("Test method isn't public: " + m.getName() + "(" + theClass.getCanonicalName() + ")"));
}
return;
}
names.add(name);
addTest(createTest(theClass, name));
}


static public Test createTest(Class<?> theClass, String name) {
Constructor<?> constructor;
try {
constructor = getTestConstructor(theClass);
} catch (NoSuchMethodException e) {
return warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()");
}
Object test;
try {
if (constructor.getParameterTypes().length == 0) {
test = constructor.newInstance(new Object[0]);
if (test instanceof TestCase) {
((TestCase) test).setName(name);
}
} else {
test = constructor.newInstance(new Object[]{name});
}
} catch (InstantiationException e) {
return (warning("Cannot instantiate test case: " + name + " (" + Throwables.getStacktrace(e) + ")"));
} catch (InvocationTargetException e) {
return (warning("Exception in constructor: " + name + " (" + Throwables.getStacktrace(e.getTargetException()) + ")"));
} catch (IllegalAccessException e) {
return (warning("Cannot access test case: " + name + " (" + Throwables.getStacktrace(e) + ")"));
}
return (Test) test;
}


如果是这么addTest的话,假设一个TestCase下有3个方法,那AddTes就会加3个对象,有点浪费。到此处TestSuite就初始化好了。

运行TestCase

public TestResult start(String[] args) throws Exception {
...
return doRun(suite, wait);
...
}
public TestResult doRun(Test suite, boolean wait) {
// 新建一个TestResult
TestResult result = createTestResult();
result.addListener(fPrinter);
long startTime = System.currentTimeMillis();
suite.run(result);
long endTime = System.currentTimeMillis();
long runTime = endTime - startTime;
fPrinter.print(result, runTime);

pause(wait);
return result;
}


public void run(TestResult result) {
for (Test each : fTests) {
if (result.shouldStop()) {
break;
}
runTest(each, result);
}
}
public void runTest(Test test, TestResult result) {
test.run(result);
}


运行TestCase

下面是junit.framework.TestCase.run方法,这里以TestResult调用了run方法。

public void run(TestResult result) {
result.run(this);
}


在TestResult的run方法中,分为三部分,startTest,runProtected,endTest。

protected void run(final TestCase test) {
startTest(test);
Protectable p = new Protectable() {
public void protect() throws Throwable {
test.runBare();
}
};
runProtected(test, p);

endTest(test);
}


在startTest方法中,记录了运行的testcase数量,并且调用了TestListener的startTest方法。TestListener有四个接口,分别是addFailure, addError, startTest, endTest。

public void startTest(Test test) {
final int count = test.countTestCases();
synchronized (this) {
fRunTests += count;
}
for (TestListener each : cloneListeners()) {
each.startTest(test);
}
}


runProtected方法,就是调用Protected接口里的protect方法。其实就是上面的test.runBare()。AssertionFailError就是Assert失败抛出的异常。

public void runProtected(final Test test, Protectable p) {
try {
p.protect();
} catch (AssertionFailedError e) {
addFailure(test, e);
} catch (ThreadDeath e) { // don't catch ThreadDeath by accident
throw e;
} catch (Throwable e) {
addError(test, e);
}
}


TestCase里的runBare方法,定义了运行测试方法的顺序,首先是setUp,然后是runTest,最后是tearDown。因为tearDown是在finally中,所以tearDown是一定会运行的。

public void runBare() throws Throwable {
Throwable exception = null;
setUp();
try {
runTest();
} catch (Throwable running) {
exception = running;
} finally {
try {
tearDown();
} catch (Throwable tearingDown) {
if (exception == null) exception = tearingDown;
}
}
if (exception != null) throw exception;
}


TestCase的runTest方法运用java的反射,调用了测试方法。并对

protected void runTest() throws Throwable {
assertNotNull("TestCase.fName cannot be null", fName); // Some VMs crash when calling getMethod(null,null);
Method runMethod = null;
try {
// use getMethod to get all public inherited
// methods. getDeclaredMethods returns all
// methods of this class but excludes the
// inherited ones.
runMethod = getClass().getMethod(fName, (Class[]) null);
} catch (NoSuchMethodException e) {
fail("Method \"" + fName + "\" not found");
}
// 测试方法必须是公有的
if (!Modifier.isPublic(runMethod.getModifiers())) {
fail("Method \"" + fName + "\" should be public");
}

try {
runMethod.invoke(this);
} catch (InvocationTargetException e) {
e.fillInStackTrace();
throw e.getTargetException();
} catch (IllegalAccessException e) {
e.fillInStackTrace();
throw e;
}
}


在测试用例跑完后,TestListener的endTest会被触发。

public void endTest(Test test) {
for (TestListener each : cloneListeners()) {
each.endTest(test);
}
}


最后TestRunner会调用fPrinter的print方法打印出结果。

参考文档:

1.分析 JUnit 框架源代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: