5.junit简介
既然我们已经对junit有了一个大致的了解,我希望能给大家提供一个稍微正式一些的编写junit测试文档的手册,明白其中的一些关键术语和概念。但我要声明的是这并不是一本完全的手册,只能认为是一本入门手册。同其他opensource的软件有同样的问题,junit的文档并没有商业软件文档的那种有规则,简洁和完全。由开发人员编写的文档总是说不太清楚问题,全整的文档需要参考"官方"指南,api手册,邮件讨论组的邮件,甚至包括源代码中及相关的注释。
事实上问题并没有那么复杂,除非你有非常特别的要求,否则,只需参考本文你就可以得到所需的大部分信息。
- 安装
首先你要获取junit的软件包,从junit下载最新的软件包(截至写作本文时,junit的最新版本是3.7)。将其在适当的目录下解包。这样在安装目录(也就是你所选择的解包的目录)下你找到一个名为junit.jar的文件。将这个jar文件加入你的classpath系统变量。(ide的设置会有所不同,参看你所喜爱的ide的配置指南)junit就安装完了。太easy了!
你一旦安装完junit,就有可能想试试我们的car和testcar类,没问题,我已经运行过了,你得到的结果应该和我列出的结果类似。(以防新版junit使我的文章过时)
接下来,你可能会先写测试代码,再写工作代码,或者相反,先写工作代码,再写测试代码。我更赞成使用前一种方法:先写测试代码,再写工作代码。因为这样可以使我们编写工作代码时清晰地了解工作类的行为。
要注意编写一定能通过的测试代码(如文中的例子)并没有任何意义,只有测试代码能帮助我们发现bug,测试代码才有其价值。此外测试代码还应该对工作代码进行全面的测试。如给方法调用的参数传入空值、错误值和正确的值,看看方法的行为是否如你所期望的那样。
你现在已经知道了编写测试类的基本步骤:
1>扩展testcase类;
2>覆盖runtest()方法(可选);
3>写一些testxxxxx()方法; - fixture
解下来的问题是,如果你要对一个或若干个的类执行多个测试,该怎么办?junit对此有特殊的解决办法。
如果需要在一个或若干个的类执行多个测试,这些类就成为了测试的context。在junit中被称为fixture(如testcar类中的 mycar 和 expectedwheels )。当你编写测试代码时,你会发现你花费了很多时间配置/初始化相关测试的fixture。将配置fixture的代码放入测试类的构造方法中并不可取,因为我们要求执行多个测试,我并不希望某个测试的结果意外地(如果这是你要求的,那就另当别论了)影响其他测试的结果。通常若干个测试会使用相同的fixture,而每个测试又各有自己需要改变的地方。
为此,junit提供了两个方法,定义在testcase类中。
protected void setup() throws java.lang.exception protected void teardown() throws java.lang.exception
覆盖setup()方法,初始化所有测试的fixture(你甚至可以在setup中建立网络连接),将每个测试略有不同的地方在testxxx()方法中进行配置。
覆盖teardown()(我总想起一首叫雨滴的吉他曲),释放你在setup()中分配的永久性资源,如数据库连接。
当junit执行测试时,它在执行每个testxxxxx()方法前都调用setup(),而在执行每个testxxxxx()方法后都调用teardown()方法,由此保证了测试不会相互影响。
- testcase
需要提醒一下,在junit.framework.assert类中定义了相当多的assert方法,主要有assert(), assert(), assertequals(), assertnull(), assertsame(), asserttrue(), fail()等方法。如果你需要比较自己定义的类,如car。assert方法需要你覆盖object类的equals()方法,以比较两个对象的不同。实践表明:如果你覆盖了object类的equals()方法,最好也覆盖object类的hashcode()方法。再进一步,连带object类的tostring()方法也一并覆盖。这样可以使测试结果更具可读性。
当你设置好了fixture后,下一步是编写所需的testxxx()方法。一定要保证testxxx()方法的public属性,否则无法通过内省(reflection)对该测试进行调用。
每个扩展的testcase类(也就是你编写的测试类)会有多个testxxx()方法。一个testxxx()方法就是一个测试。要想运行这个测试,你必须定义如何运行该测试。如果你有多个testxxx()方法,你就要定义多次。junit支持两种运行单个测试的方法:静态的和动态的方法。
静态的方法就是覆盖testcase类的runtest()方法,一般是采用内部类的方式创建一个测试实例:
testcase test01 = new testcar("test getwheels") { public void runtest() { testgetwheels(); } }采用静态的方法要注意要给每个测试一个名字(这个名字可以任意起,但你肯定希望这个名字有某种意义),这样你就可以区分那个测试失败了。
动态的方法是用内省来实现runtest()以创建一个测试实例。这要求测试的名字就是需要调用的测试方法的名字:
testcase test01 = new testcar("testgetwheels");junit会动态查找并调用指定的测试方法。动态的方法很简洁,但如果你键入了错误的名字就会得到一个令人奇怪的nosuchmethodexception异常。动态的方法和静态的方法都很好,你可以按照自己的喜好来选择。(先别着急选择,后面还有一种更酷的方法等着你呢。)
- testsuite
一旦你创建了一些测试实例,下一步就是要让他们能一起运行。我们必须定义一个testsuite。在junit中,这就要求你在testcase类中定义一个静态的suite()方法。suite()方法就像main()方法一样,junit用它来执行测试。在suite()方法中,你将测试实例加到一个testsuite对象中,并返回这个testsuite对象。一个testsuite对象可以运行一组测试。testsuite和testcase都实现了test接口(interface),而test接口定义了运行测试所需的方法。这就允许你用testcase和testsuite的组合创建一个testsuite。这就是为什么我们前面说testcase,testsuite以及testsuite组成了一个composite pattern的原因。例子如下:
public static test suite() { testsuite suite= new testsuite(); suite.addtest(new testcar("testgetwheels")); suite.addtest(new testcar("testgetseats")); return suite; }从junit 2.0开始,有一种更简单的动态定义测试实例的方法。你只需将类传递给testsuite,junit会根据测试方法名自动创建相应的测试实例。所以你的测试方法最好取名为testxxx()。例子如下:
public static test suite() { return new testsuite(testcar.class); }从junit的设计我们可看出,junit不仅可用于单元测试,也可用于集成测试。关于如何用junit进行集成测试请参考相关资料。
为了兼容性的考虑,下面列出使用静态方法的例子:
public static test suite() { testsuite suite= new testsuite(); suite.addtest( new testcar("getwheels") { protected void runtest() { testgetwheels(); } } ); suite.addtest( new testcar("getseats") { protected void runtest() { testgetseats(); } } ); return suite; } - testrunner
有了testsuite我们就可以运行这些测试了,junit提供了三种界面来运行测试
[text ui] junit.textui.testrunner [awt ui] junit.awtui.testrunner [swing ui] junit.swingui.testrunner
我们前面已经看过文本界面了,下面让我们来看一看图形界面:
界面很简单,键入类名-testcar。或在启动ui的时候键入类名:
[windows] d:>java junit.swingui.testrunner testcar [unix] % java junit.swingui.testrunner testcar
从图形ui可以更好的运行测试可查单测试结果。还有一个问题需要注意:如果junit报告了测试没有成功,junit会区分失败(failures)和错误(errors)。失败是一个期望的被assert方法检查到的结果。而错误则是意外的问题引起的,如arrayindexoutofboundsexception。
由于testrunner十分简单,界面也比较直观,故不多介绍。朋友们可自行参考相关资料。
