您的位置:首页 > 编程语言 > Java开发

JUNIT之COMMAND模式学习笔记

2014-12-21 17:02 369 查看
文章摘要:前篇文章大体介绍了JUNIT的运行原理的冰山一角,当然对于大婶写的代码要仔细的研读才会有收获。前文对于COMMAND模式在JUNIT中的体现也只是一笔带过,没有多提,现在回味起来难免赶脚有很多地方没有交代清楚,所以,今天又把JUNIT源码翻出来仔细看了一看,希望有所收获,所以有了此文记录下来。一,什么是COMMAND模式(略)二,JUNIT中使用COMMAND模式对于第一个问题,笔者也是找度娘请教,也有很多参考书籍可以参考,这里就不浪费敲键盘时间;本文重点就放在第二个问题上。这里还是从JUNIT自带案例入手,来看看JUNIT笔者理解的COMMAND模式,案例部分代码如下:public static void main(String[] args) {        junit.textui.TestRunner.run(suite());    }    public static Test suite() {        TestSuite suite = new TestSuite("Framework Tests");        suite.addTestSuite(TestCaseTest.class);        suite.addTest(SuiteTest.suite()); // Tests suite building, so can't use automatic test extraction        suite.addTestSuite(TestListenerTest.class);        suite.addTestSuite(AssertionFailedErrorTest.class);        suite.addTestSuite(AssertTest.class);        suite.addTestSuite(TestImplementorTest.class);        suite.addTestSuite(NoArgTestCaseTest.class);        suite.addTestSuite(ComparisonCompactorTest.class);        suite.addTestSuite(ComparisonFailureTest.class);        suite.addTestSuite(DoublePrecisionAssertTest.class);        suite.addTestSuite(FloatAssertTest.class);        return suite;    } 对这一段代码笔者简述一下自己的认识,首先,一个入口junit.textui.TestRunner的run方法去执行对应的execute,在JUNIT中的execute是一个Test接口,代码如下:package junit.framework;/*** 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);}
Test接口只有两个方法,一个是countTestCases用于统计已执行的方法个数;第二个是run用于执行对应的方法。那么实现Test接口有哪些组件呢?有:TestSuite和TestCase
至此,JUNIT中COMMAND命令模式我们应该有点点感觉了,也就是说,所有suite和testcase都有对应需要执行的命令方法run,通过一个入口run,依次执行。
<span style="white-space:pre">	</span>回到我们的案例代码中来,结合具体实例我们才能说清楚问题
在实例部分代码中,有一个suite方法和一个main方法,这个junit自带的实例可以直接运行,运行结果就不贴出。
<pre name="code" class="java">public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
这段代码和相关的执行测试方法代码已经在上一篇文章里面提到过,这里就不再重复。这里笔者认为我们已经有了JUNIT的运行机制感觉,了解TestSuite类如果添加测试方法基本上对于JUNIT的COMMAND模式也会有个入门认识。对于TestSuite实例代码中添加需要测试类的方法有两种类型:1,直接添加类.class;2,通过TestSuite添加。我们来看看TestSuite的完整代码:package junit.framework;import java.io.PrintWriter;import java.io.StringWriter;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Modifier;import java.util.ArrayList;import java.util.Enumeration;import java.util.List;import java.util.Vector;import org.junit.internal.MethodSorter;/*** A <code>TestSuite</code> is a <code>Composite</code> of Tests.* It runs a collection of test cases. Here is an example using* the dynamic test definition.* <pre>* TestSuite suite= new TestSuite();* suite.addTest(new MathTest("testAdd"));* suite.addTest(new MathTest("testDivideByZero"));* </pre>* <p>* Alternatively, a TestSuite can extract the tests to be run automatically.* To do so you pass the class of your TestCase class to the* TestSuite constructor.* <pre>* TestSuite suite= new TestSuite(MathTest.class);* </pre>* <p>* This constructor creates a suite with all the methods* starting with "test" that take no arguments.* <p>* A final option is to do the same for a large array of test classes.* <pre>* Class[] testClasses = { MathTest.class, AnotherTest.class }* TestSuite suite= new TestSuite(testClasses);* </pre>** @see Test*/public class TestSuite implements Test {/*** ...as the moon sets over the early morning Merlin, Oregon* mountains, our intrepid adventurers type...*/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 + " (" + exceptionToString(e) + ")"));} catch (InvocationTargetException e) {return (warning("Exception in constructor: " + name + " (" + exceptionToString(e.getTargetException()) + ")"));} catch (IllegalAccessException e) {return (warning("Cannot access test case: " + name + " (" + exceptionToString(e) + ")"));}return (Test) test;}/*** Gets a constructor which takes a single String as* its argument or a no arg constructor.*/public static Constructor<?> getTestConstructor(Class<?> theClass) throws NoSuchMethodException {try {return theClass.getConstructor(String.class);} catch (NoSuchMethodException e) {// fall through}return theClass.getConst4000ructor();}/*** Returns a test which will fail and log a warning message.*/public static Test warning(final String message) {return new TestCase("warning") {@Overrideprotected void runTest() {fail(message);}};}/*** Converts the stack trace into a string*/private static String exceptionToString(Throwable e) {StringWriter stringWriter = new StringWriter();PrintWriter writer = new PrintWriter(stringWriter);e.printStackTrace(writer);return stringWriter.toString();}private String fName;private Vector<Test> fTests = new Vector<Test>(10); // 这个是没一个run命令所需要执行的所有测试Test/*** Constructs an empty TestSuite.*/public TestSuite() {}/*** Constructs a TestSuite from the given class. Adds all the methods* starting with "test" as test cases to the suite.* Parts of this method were written at 2337 meters in the Hueffihuette,* Kanton Uri*/public TestSuite(final Class<?> theClass) {addTestsFromTestCase(theClass);}private void addTestsFromTestCase(final Class<?> theClass) {fName = theClass.getName();try {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;}if (!Modifier.isPublic(theClass.getModifiers())) {addTest(warning("Class " + theClass.getName() + " is not public"));return;}Class<?> superClass = theClass;List<String> names = new ArrayList<String>();while (Test.class.isAssignableFrom(superClass)) {for (Method each : MethodSorter.getDeclaredMethods(superClass)) {addTestMethod(each, names, theClass);}superClass = superClass.getSuperclass();}if (fTests.size() == 0) {addTest(warning("No tests found in " + theClass.getName()));}}/*** Constructs a TestSuite from the given class with the given name.** @see TestSuite#TestSuite(Class)*/public TestSuite(Class<? extends TestCase> theClass, String name) {this(theClass);setName(name);}/*** Constructs an empty TestSuite.*/public TestSuite(String name) {setName(name);}/*** Constructs a TestSuite from the given array of classes.* 通过class数组对象构造一个TestSuite* @param classes {@link TestCase}s*/public TestSuite(Class<?>... classes) {for (Class<?> each : classes) {addTest(testCaseForClass(each));}}private Test testCaseForClass(Class<?> each) {if (TestCase.class.isAssignableFrom(each)) {return new TestSuite(each.asSubclass(TestCase.class));} else {return warning(each.getCanonicalName() + " does not extend TestCase");}}/*** Constructs a TestSuite from the given array of classes with the given name.** @see TestSuite#TestSuite(Class[])*/public TestSuite(Class<? extends TestCase>[] classes, String name) {this(classes);setName(name);}/*** Adds a test to the suite.*/public void addTest(Test test) {fTests.add(test);}/*** Adds the tests from the given class to the suite*/public void addTestSuite(Class<? extends TestCase> testClass) {addTest(new TestSuite(testClass));}/*** Counts the number of test cases that will be run by this test.*/public int countTestCases() {int count = 0;for (Test each : fTests) {count += each.countTestCases();}return count;}/*** Returns the name of the suite. Not all* test suites have a name and this method* can return null.*/public String getName() {return fName;}/*** Runs the tests and collects their result in a TestResult.*/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);}/*** Sets the name of the suite.** @param name the name to set*/public void setName(String name) {fName = name;}/*** Returns the test at the given index*/public Test testAt(int index) {return fTests.get(index);}/*** Returns the number of tests in this suite*/public int testCount() {return fTests.size();}/*** Returns the tests as an enumeration*/public Enumeration<Test> tests() {return fTests.elements();}/***/@Overridepublic String toString() {if (getName() != null) {return getName();}return super.toString();}private void addTestMethod(Method m, List<String> names, Class<?> theClass) {String name = m.getName();if (names.contains(name)) {return;}if (!isPublicTestMethod(m)) {if (isTestMethod(m)) {addTest(warning("Test method isn't public: " + m.getName() + "(" + theClass.getCanonicalName() + ")"));}return;}names.add(name);addTest(createTest(theClass, name));//通过构造器生成测试方法的类并通过fName属性保存待测试的方法}private boolean isPublicTestMethod(Method m) {return isTestMethod(m) && Modifier.isPublic(m.getModifiers());}private boolean isTestMethod(Method m) {return m.getParameterTypes().length == 0 &&m.getName().startsWith("test") &&m.getReturnType().equals(Void.TYPE);}}源码中有几个地方我们需要注意,笔者直接在源码中添加备注。我们重点看看TestSuite类的run方法,其中runTest方法中参数test参数是COMMAND命令的关键,它可以是一个TestSuite也可以是一个TestCase,好,到了这里这篇文章进入高潮部分了,这里贴出TestCase和COMMAND相关源代码:public TestResult run() {TestResult result = createResult();run(result);return result;}/*** Runs the test case and collects the results in TestResult.*/public void run(TestResult result) {result.run(this);}/*** Runs the bare test sequence.** @throws Throwable if any exception is thrown*/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;}/*** Override to run the test and assert its state.** @throws Throwable if any exception is thrown*/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;}}在TestCase类中关于run的方法最多,最终实际运行的也是runTest这个方法,其他相关run相关方法也体现了JUNIT一些机制,有兴趣读者可以自己慢慢研读。到了这里我们先来做一个总结,经过上面两个重要类的代码阅读理解,COMMAND模式笔者用自己语言这样理解:所有COMMAND都是Test作为顶级接口,通过TestSuite进行包装需要执行的下一步COMMAND,最终都是通过构造TestCase这个类并添加待测试方法执行COMMAND。
                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 框架 结构 command