这一部分是Junit工具的简单使用说明,作为作业指导书的附件。
本文简单地介绍了如何使用Junit编写和组织自己的单元测试代码。
l 简单的测试用例
如何写测试代码呢?最简单的方式当然是在调试器中写成表达式的形式。你不必重新编译程序就可以改变调试表达式,并且你可以等看到了具体运行的对象再决定写什么样的调试表达式。另一种方式就是编写调试表达式输出一些东西到标准的输出流。这两种调试方式都有限制,因为它们需要你自己去分析产生的结果。同时,它们在组织方面也不是很好。比如,在第一种情况下一次仅仅可以执行一个调试表达式;在第二种情况下如果一个程序很多的输出语句,那将是一件可怕的事情。
Junit不需要你自己去判断正误,并且一次可以执行多个测试。要用Junit进行测试,你要做下面的事情:
1. 创建一个TestCase类型的实例。
2. 重载该类中的runTest()方法。
3. 如果要检查一个值是否正确,调用assert()方法并传送一个boolean类型的参值,如果该参数是true,则说明测试是成功的。
举例说明,要测试两个有相同currency属性的Money类型的对象相加。
Public void testSimpleAdd() {
Money m12CHF = new Money(12, “CHF”);
Money m14CHF = new Money(14, “CHF”);
Money expected = new Money(26, “CHF”);
Money result = m12CHF.add(m14CHF);
Assert(expected.equals(result));
}
如果你想做的测试和原来做过的很相近,就使用Fixture。如果你想执行多个测试,就创建一个Suite。
l Fixture
如果你有多个测试作用在相同或相近的对象集合上,该怎么做呢?
执行测试需要依赖已知的一些对象。这些对象的集合就称为Fixture。当你做测试时,你可能常常会发现花费了很多的时间去构建fixture,而不是真正的测试数据。
通常地,同一fixture可以被用来做几个不同的测试。每一个测试用例传送不同的信息或者参数到fixture中从而得到不同的测试结果。
下面说明了如何建立通用的fixture:
1. 创建一个TestCase的子类。
2. 给fixture的每一部分添加一个变量实例。
3. 重载setUp()方法来初始化变量。
4. 重载tearDown()方法来释放在setUp()方法中分配的参数资源。
举例说明:如果几个不同的测试用例都要用到12Swiss Frances,14Swiss Frances和28US Dollars,那么首先创建一个fixture。
Public class MoneyTest extends TestCase {
Private Money f12CHF;
Private Money f14CHF;
Private Money f28USD;
Protected void setUp() {
F12CHF = new Money(12, “CHF”);
F14CHF = new Money(14, “CHF”);
F28USD = new Money(28, “USD”);
}
}
一旦建立了Fixture,接下来就可以随便写多少测试了。
l Test Case
当fixture创建后,如何编写并且调用一个单独的测试用例呢?
如果没有fixture,写一个测试用例很简单——在TestCase的匿名子类中重载runTest方法就可以了。
Junit提供了一个很简便的方式写关于一个fixture的测试。下面就是你要做的:
1. 在fixture类中写测试用例的方法。注意要保证它是public类型的,否则它不能被映射(reflection)所调用。
2. 创建一个TestCase的实例,并且传送测试用例方法的名字给构造函数。
举例说明,要测试一个Money类型的实例和一个MoneyBag类型的实例相加。
public void testMoneyMoneyBag() {
Money bag[] = {f26CHF, f28USD};
MoneyBag expected = new MoneyBag(bag);
AssertEquals(expected, f12CHF.add(f28USD.add(f14CHF)));
}
创建一个MoneyTest的实例,这个实例将运行此测试用例:
new MoneyTest(“testMoneyMoneyBag”);
当测试执行的时候,测试的名字被用来查找要执行的方法。
如果你有几个测试,就组织它们成为一个Suite。
l Suite
如果一起执行多个测试呢?
如果你有两个测试,你当然想同时执行它们。Junit提供了一个对象——TestSuite,这个对象可以将任意数量的测试用例放在一个执行。
比如,要运行一个单独的测试用例,你可以运行:
TestResult result = (new MoneyTest(“testMoneyMoneyBag”)).run();
要创建有两个用例的suite并运行它们就可以:
TestSuite suite = new TestSuite();
Suite.addTest(new MoneyTest(“testMoneyEquals”));
Suite.addTest(new MoneyTest(“testSimpleAdd”));
TestResult result = suite.run();
另一个方式是让Junit来从一个TestCase中执行一个suite。要这样做,你将你的TestCase的类作为参数传送给TestSuite的构造函数。
TestSuite suite = new TestSuite(MoneyTest.class);
TestResult result = suite.run();
如果你只是要执行包含了测试用例的子集时就使用手工的方式。否则最好使用自动suite抽取的方式。这样避免了你在增加一个新的测试用例的时候更新suite的创建代码。
TestSuite不仅可以包含TestCase,实际上,只要实现了Test接口的对象都可以加入其中。比如,可以在你的代码中创建一个TestSuite,我在我代码中也创建一个,我们可以通过创建一个TestSuite来包含它们,从而一起执行。
TestSuite suite = new TestSuite();
Suite.addTest(Kent.suite());
Suite.addTest(Erich.suite());
TestResult result = suite.run();
l TestRunner
如何运行测试并收集结果?
一旦建立了TestSuite,就可以运行它了。Junit提供了运行suite并显示结果的工具。你要让你的suite对TestRunner来说是可以访问的,设置一个静态的方法suite(),这个方法要返回一个TestSuite。
比如,使一个MoneyTest suite对TestRunner来说是可以访问的,然后在你的MoneyTest中加入下面的代码:
public static Test suite() {
TestSuite suite = new TestSuite();
Suite.addTest(new MoneyTest(“testMoneyEquals”));
Suite.addTest(new MoneyTest(“testSimpleAdd”));
Return suite;
}
如果一个TestCase类没有定义一个suite()方法,那么TestRunner将提取出一个suite出来,并在suite加入所有的”test”开头的方法。
Junit提供了两个图形和一个文本格式的TestRunner工具。运行图形工具可以键入java junit.swtui.TestRunner或者junit.swingui.TestRunner。图形用户界面提供了一个窗口,包括:
l 键入suite方法所在的类的名字。
l 一个运行按钮,可以执行测试。
l 一个表示运行状态的进度条。
l 失败测试的列表。
如果测试没有成功,则失败的测试结果将在底部的列表中显示出来。Junit将failutue和error分开来说。Failure是可以预料的并且用assertion来检查的。Error是不可预料的就像ArrayIndexOutOfBounds等。下图显示了一个失败测试的例子:
要找出更多的failure或者error,可以点击”Show”按钮。下图显示了一个failure的运行堆栈。
Junit还提供了一个分批处理的接口。键入java junit.textui.TestRunner,接着加上包括了suite()方法的类的名字。这个接口将结果以文本的方式输出出来。当然你可以定义一个main()方法,然后调用batch接口。
比如:对MoneyTest来说运行batch TestRunner,可以:
public static void main(String args[]) {
junit.textui.TestRunner.run(suite());
}
有了main()方法,你可以运行测试,仅仅在系统提示符后键入java MoneyTest就可以了。
要使用图形RunnerTest或者文本的TestRunner,你要注意junit.jar要放到你系统的CLASSPATH中。