四、 使用JMUnit
a) 建立JMUnit
在下载JMUnit后,请确保相应的两个JMUnit .jar文件(JMUnit4CLDC10.jar和JMUnit4CLDC11.jar)可用于classpath中。注意,这个参数既针对你的
Java ME编译器也针对运行时刻环境或IDE。当前,JMUnit的发行版本是1.0.2。
b) JMUnit测试用例
JMUnit提供了两个版本的框架(每个版本都位于各自的JAR内);一个用于CLDC 1.0应用程序,另一个用于CLDC 1.1应用程序(其中,支持浮点原型)。按照典型的JUnit惯例,使用JMUnit创建适当的单元测试的第一步是创建一个测试用例。为了在JMUnit中创建一个测试用例,你必须创建一个新的派生自JMUnit的jmunit.framework.cldc10.TestCase或jmunit.framework.cldc11.TestCase的测试用例类。正如其包名所暗示的,一个支持1.0版本的CLDC,另一个支持1.1版本的CLDC。唯一的区别是,在assertEquals()和assertNotEquals()方法(见下面)的cldc11.TestCase实现中支持Java浮点原型。
按照JUnit习惯,一个测试用例类应该包含要测试的类名,并且以"Test"结束。因此,一个测试上面这个温度转换类的简单的CLDC 1.1版本的JMUnit测试用例可以按如下方式定义:
public class TemperatureConversionTest extends jmunit.framework.cldc11.TestCase {} |
所有的测试方法必须位于一个测试用例类之内。而且,按照惯例,测试方法名都以"test"开头,然后根据被测试的类中的方法进行命名。例如,一个测试fahrenheitToCelsius方法的测试用例方法应该为testfahrenheitToCelsius。每一个测试方法必须"断言"期望的结果。对于那些不熟悉JUnit测试的
开发者来说,一个断言其实就是一个语句,它负责验证或证明从某个方法执行中程序员所期望的结果。JMUnit支持下列断言:
assertTrue(expression) assertFalse(expression) assertSame(expected,actual) assertNotSame(expected,actual) assertEquals(expected,actual) assertNotEquals(expected,actual) assertNull(object) assertNotNull(object) |
在JMUnit中,任何使用这些断言调用之一的测试方法都必须抛出一个AssertionFailedException异常。框架使用该异常来标识失败的测试。现在,这个添加了适当测试方法的TemperatureConversionTest类看起来如下所示。
import jmunit.framework.cldc11.*; public class TemperatureConversionTest extends TestCase { public void testfahrenheitToCelsius() throws AssertionFailedException{ System.out.println("fahrenheitToCelsius"); float result = TemperatureConversion.fahrenheitToCelsius(66F); assertEquals(18.88889F,result); } public void testcelsiusToFahrenheit() throws AssertionFailedException{ System.out.println("celsiusToFahrenheit"); float result = TemperatureConversion.celsiusToFahrenheit(20F); assertEquals(68F, result); } public void testisHotter() throws AssertionFailedException { System.out.println("isHotter"); assertTrue(TemperatureConversion.isHotter(70F,2F)); } public void testisCooler() throws AssertionFailedException { System.out.println("isCooler"); assertTrue(TemperatureConversion.isCooler(10F,10F)); } } |
对于每一个标准的JUnit实现,JMUnit测试用例抽象类都提供了setup()和tearDown()方法,这两个方法都能够被重载并用于初始化,并在经由测试用例运行测试前后用来清除任何对象或资源。例如,在Java ME应用程序中,setup()可以用于在测试前打开一个记录存储,而tearDown()用于在测试后关闭记录存储。除了setup和tearDown方法外,还有一个fail()方法用于实现-无论assert语句显示什么内容,都允许一个测试方法返回一个测试失败。这个方法经常用于一个测试方法内的某些条件中,或用于作为未开发的单元测试的一个代理,从而作为一种方式来指示尚待完成的工作。
JMUnit中的每一个测试用例类都有一个相应的构造器。因此,派生自JMUnit的测试用例类的构造器必须调用超类构造器,并传入一个整数以指示在该测试用例中的测试个数,还要传入一个字符串来标识该测试用例。
public TemperatureConversionTest() { super(4,"TemperatureConversionTest"); } |
这个整数指示测试的个数必须匹配测试用例中的实际测试的数目。确保你传入构造器的测试的个数匹配测试用例中的实际的测试的个数是相当重要的。当你
分析该测试用例的test(int testNumber)方法,就会看到它们之间的
关系。
测试用例中的这个test(int testNumber)方法负责"剔除"测试方法。因为Java ME缺乏映射能力,所以不能象在JUnit中一样,找到test方法并自动地执行之。因此,每一个测试方法必须被添加到该test方法中的一个switch语句中,并且基于一个测试号进行相应的调用。在我们的TemperatureConversionTest情况下,这个test方法看起来如下列代码所示:
public void test(int testNumber) throws Throwable { switch(testNumber) { case 0:testfahrenheitToCelsius();break; case 1:testcelsiusToFahrenheit();break; case 2:testisHotter();break; case 3:testisCooler();break; default: break; } } |
这也正解释了为什么你必须向测试用例构造器提供一个测试号。在运行时刻,JMUnit框架创建一个测试用例类的实例。然后,框架在一个循环内调用该测试用例实例的每一个测试方法。通过这种方式,测试方法的switch语句中的每一个case语句(以及相应的每一个测试)都会被框架所调用。当把一个测试方法添加到测试用例中时,如果忘记更新测试用例类的构造器可能会导致部分测试用例不被激活。
因为你使用JMUnit编写测试方法,那么这可能会比基于JUnit更灵活一些:它允许执行测试方法。借助于测试方法的控制作用,你可以编写使用参数的测试-而这一点JUnit是不允许(由于反射
机制)的。例如,针对TemperatureConverstionTest方法的一个测试方法可能看起来如下所示:
public void testcelsiusToFahrenheit(float c, float f) throws AssertionFailedException{ System.out.println("celsiusToFahrenheit(float c)"); float result = TemperatureConversion.celsiusToFahrenheit(c); assertEquals(f, result); } |
然后,该测试方法就可以使用参数来调用switch语句中的这个测试方法。
public void test(int testNumber) throws Throwable { switch(testNumber) { case 0:testfahrenheitToCelsius();break; case 1:testcelsiusToFahrenheit();break; case 2:testisHotter();break; case 3:testisCooler();break; case 4:testcelsiusToFahrenheit(20F,68F);break; default: break; } } |
c) JMUnit测试集
测试集负责管理一个或多个测试用例。JMUnit提供了两个测试集抽象类(jmunit.framework.cldc10.TestSuite和jmunit.framework.cldc11.TestSuite),你可以从它们进行继承以便创建一个测试集。就象测试用例一样,你应该继承的测试集的类型依赖于你在使用哪一个版本的CLDC。cldc10.TestSuit适用于CLDC 1.0应用程序,而cldc11.TestSuite适用于CLDC 1.1应用程序。这两个测试集抽象类都分别提供了一个以一个字符串作为参数的构造器。该字符串用于给出测试集的一个描述。
一个测试集的唯一功能是创建它的所有测试用例的一个实例,然后调用这些测试用例的测试方法。为了在一个测试集上添加一个测试用例,在构建测试集时应该添加add(testCase)方法。下面是一个实现转换测试用例的测试集的例子:
import jmunit.framework.cldc11.TestSuite; public class ConversionTestSuite extends TestSuite{ public ConversionTestSuite() { super("All Conversion Tests"); add(new DistanceConversionTest()); add(new TemperatureConversionTest()); } |
d) 执行JMUnit测试
JMUnit的TestCase和TestSuite抽象类都是MIDlet的子类。这允许你在一个仿真器(也有可能是一个真实设备)中运行你的单个测试用例或测试集。当在一个模拟器上运行时,每一个测试用例或测试集都提供两个命令:exit和test。图1描述了上面描述的测试集相应的执行结果;图2展示了失败时显示的内容。
图1.执行一个测试用例:执行一个JMUnit测试集使你能够选择退出或测试该测试集。测试集的结果以图形方式显示。 图2.一个失败测试用例:当一个测试用例失败时,失败情况以红色图形方式显示。 图3.失败测试用例的控制台输出:失败时的文本输出指出哪个测试用例失败了,为什么它失败,并且提供一个堆栈跟踪结果以帮助确定它在哪儿失败的。 |
因此,当执行测试时,你还要检查该控制台(见图3)。失败
信息通过控制台以更好的文档形式输出。这些失败输出包括堆栈跟踪信息,还有来自于该测试的实际的和期望的值。比较于随后我们将讨论的J2MEUnit,这可能是JMUnit所缺乏的特征之一。在J2MEUnit中,不是使用控制台输出,测试用例失败情形将被显示到模拟设备上。
查看本文来源