`
xo_tobacoo
  • 浏览: 383520 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

Junit入门和基本运行结构分析

    博客分类:
  • j2ee
阅读更多

The test code must be written to do a few things:

一、测试流程:

1 Setup all conditions needed for testing (create any re-quired objects, allocate any needed resources, etc.)

初始化资源

2 Call the method to be tested

调用方法开始测试

3 Verify that the method to be tested functioned as ex-Pected

验证被测试方法是否按期望方式运行

4 Clean up after itself

完成测试后自行清理资源

 

下面让以上述为依据看看testCase的源代码:

public abstract class TestCase extends Assert implements Test {

       /**

        * the name of the test case测试用例名称

        */

       private String fName;

 

       /**

        * No-arg constructor to enable serialization. This method 构造函数

        * is not intended to be used by mere mortals without calling setName().

        */

       public TestCase() {

              fName= null;

       }

       /**

        * Constructs a test case with the given name.构造函数

        */

       public TestCase(String name) {

              fName= name;

       }

       /**

        * Counts the number of test cases executed by run(TestResult result).记录测试用例数目

        */

       public int countTestCases() {

              return 1;

       }

       /**

        * Creates a default TestResult object//创建默认TestResult对象,里面存储测试结果

        *

        * @see TestResult

        */

       protected TestResult createResult() {

           return new TestResult();

       }

       /**

        * A convenience method to run this test, collecting the results with a

        * default TestResult object.执行测试很简易的方法,使用默认的TestResult

        *

        * @see TestResult

        */

       public TestResult run() {

              TestResult result= createResult();

              run(result);

              return result;

       }

       /**

        * Runs the test case and collects the results in TestResult.执行测试并把手机测试结果信息存入指定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;

              }

       }

       /**

        * Sets up the fixture, for example, open a network connection.

        * This method is called before a test is executed. 测试前的初始化方法

        */

       protected void setUp() throws Exception {

       }

       /**

        * Tears down the fixture, for example, close a network connection.

        * This method is called after a test is executed. 测试后的销毁方法

        */

       protected void tearDown() throws Exception {

       }

       /**

        * Returns a string representation of the test case

        */

       @Override

       public String toString() {

           return getName() + "(" + getClass().getName() + ")";

       }

       /**

        * Gets the name of a TestCase返回测试案例的名称

        * @return the name of the TestCase

        */

       public String getName() {

              return fName;

       }

       /**

        * Sets the name of a TestCase设定测试案例的名称

        * @param name the name to set

        */

       public void setName(String name) {

              fName= name;

       }

}

 

二、运行方式:

您定义自己的TestCase,并使用TestRunner来运行测试,事实上TestRunner并不直接运行 TestCase上的单元方法,而是透过TestSuiteTestSuite可以将数个TestCase在一起,而让每个TestCase保持简单。

 

public class SimpleTest extends TestCase {

       protected int fValue1;

       protected int fValue2;

 

        SimpleTest(String name){

        super(name);

        }

       @Override

       protected void setUp() {

              fValue1= 2;

              fValue2= 3;

       }

      

       public void testAdd() {

              double result= fValue1 + fValue2;

              // forced failure result == 5

              assertTrue(result == 6);

       }

       public void testDivideByZero() {

              int zero= 0;

              int result= 8/zero;

              result++; // avoid warning for not using result

       }

       public void testEquals() {

              assertEquals(12, 12);

              assertEquals(12L, 12L);

              assertEquals(new Long(12), new Long(12));

 

              assertEquals("Size", 12, 13);

              assertEquals("Capacity", 12.0, 11.99, 0.0);

       }

       public static void main (String[] args) {

              junit.textui.TestRunner.run(suite());

       }

}

在这个例子中,您并没有看到任何的TestSuite,事实上,如果您没有提供任何的TestSuiteTestRunner会自己建立一个,然后这个 TestSuite会使用反射(reflection)自动找出testXXX()方法。
如果您要自行生成TestSuite,则在继承TestCase之后,提供静态的(static)的suite()方法,例如:
 public static Test suite() {

 

              /*

               * the type safe way

               **/

              TestSuite suite= new TestSuite();

              suite.addTest(

                     new SimpleTest("add") {

            @Override

                             protected void runTest() { testAdd(); }

                     }

              );

 

              suite.addTest(

                     new SimpleTest("testDivideByZero") {

            @Override

                             protected void runTest() { testDivideByZero(); }

                     }

              );

              return suite;

             

 

              /*

               * the dynamic way

               */

              //return new TestSuite(SimpleTest.class);

       }
JUnit
并没有规定您一定要使用testXXX()这样的方式来命名您的测试方法,如果您要提供自己的方法(当然JUnit 鼓励您使用testXXX()这样的方法名称),则可以如上撰写,为了要能够使用建构函式提供测试方法名称,您的TestCase必须提供如下的建构函式:
public SimpleTest (String testMethod) {
  super(testMethod);
}

如果要加入更多的测试方法,使用addTest()就可以了,suite()方法传回一个TestSuite物件,它与 TestCase都实作了Test介面,TestRunner会调用TestSuite上的run()方法,然后TestSuite会将之委託给 TestCase上的run()方法,并执行每一个testXXX()方法。
除了组合TestCase之外,您还可以将数个TestSuite组合在一起,例如:
public static Test suite() {
  TestSuite suite= new TestSuite();
  suite.addTestSuite(TestCase1.class);
  suite.addTestSuite(TestCase2.class);
  return suite;
}

如此之来,您可以一次运行所有的测试,而不必个别的运行每一个测试案例,您可以写一个运行全部测试的主测试,而在使用TestRunner时呼叫 suite()方法,例如:
junit.textui.TestRunner.run(TestAll.suite());

TestCase
TestSuite都实作了Test介面,其运行方式为 Command 模式 的一个实例,而TestSuite可以组合数个TestSuiteTestCase,这是 Composite 模式的一个实例。

 

 

三、失败和错误的区别

通常情况下是不希望自己编写的Java代码抛出错误的,而只应该抛出异常。通常来说,应该让Java虚拟机本身来抛出错误。因为一个错误意味着低级别的、不可恢复的问题,例如:无法装载一个类,这种问题我们也不期望被恢复。出于这个原因,也许我们对JUnit通过抛出错误来表示一个断言的失败而感到有些奇怪。

JUnit抛出的是错误而不是异常,这是因为在Java中,错误是不需要检查的;因此,不是每个方法都必须声明它会抛出哪些错误。你可能会认为RuntimeException可以完成相同的功能,但是如果JUnit抛出这种在产品代码中可能抛出的异常的类型,JUnit测试就会和你的产品相互影响。这种影响会降低JUnit的价值。

当你的测试包含了一个失败的断言,JUnit会将它算做一个失败的测试;但是如果你的测试抛出了一个异常(并且没有捕获它),JUnit将它看成是一个错误。这个区别是很细微的,却是非常有用的:一个失败的断言通常表示产品代码中有问题,而一个错误却表示测试本身或周围的环境存在着问题。也许你的测试期望了一个不该期望的错误的异常对象,或者错误地在一个null的应用上调用了一个函数。也有可能磁盘已经满了,或者网络连接不可用,或者是一个文件找不到了。JUnit并不把这个算做产品代码的缺陷,因此它只是在表达,有些不对劲了。我不能分辨这个测试是否通过。请解决这个问题并再重新测试一次。这就是失败和错误的区别。

JUnit的测试运行器会报告一个测试运行的结果,格式如下:“78 run, 2 failures,1 error从这个结果可以判断有75个测试已经通过,有两个失败,还有一个没有结论。我们的建议是先去调查那个错误,解决了此问题以后再重新运行这个测试。也许在解决了这个错误以后,所有的测试都通过了!

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics