はじめに
単体テストは、Java Standard Edition(SE)およびEnterprise Edition(EE)の大部分のアプリケーション開発の基本要素になっています。テスト駆動型の環境を採用しているアプリケーションでは特にそうです。Kent BeckとEric GammaのオリジナルのSmalltalkフレームワークは大いに人気を博して成功を収めたので、さまざまなプログラミング言語に移植されました。その中には、たとえばAda(AUnit)、C#(NUnit)、Python(PyUnit)、Fortran(fUnit)などがあります。おそらく、JavaのJUnitは最も成功した単体テストフレームワークでしょう。そして、拡張という形で多数の子孫を生み出しており、マルチスレッドJavaアプリケーションから強力なエンタープライズJavaアプリケーションに至るまで、あらゆるものを単体テストするのに役立っています。
とはいえ、Java Micro Edition(ME)開発にJUnitを使用したり、そのためのJUnit拡張を見つけたりするのは少々大変でした。JUnitフレームワークはJavaリフレクションに依存しています。Java ME環境にはリフレクションAPIがないので、リフレクションへの依存度が高い典型的なJUnitツールは役に立ちません。しかしこの悪条件にもかかわらず、特にデバイスアプリケーション開発者向けに作られたJava ME JUnit拡張が2つ公開されています(後ほど触れますが、近いうちに1つになります)。さらに重要なのは、NetBeansとNetBeans Mobility Packにおいて、Java ME JUnitスタイルのフレームワークをNetBeansおよびNetBeans Mobility 5.5のリリースと合体させる計画があることです。このIDEでは、MEアプリケーションに単体テストをすばやく追加できるようになります。
本稿では、Java ME JUnitフレームワークを使ったJUnitテストを紹介します。このフレームワークをどこから入手するか、どうやってテストを実施するか、どのように使えば質の高いソフトウェアを構築できるかを説明します。
Java ME単体テストフレームワークの入手
現時点で入手できるJava ME単体テストフレームワークは、J2MEUnitとJMUnitの2つです。どちらのプロジェクトもオープンソースフレームワークであり、SourceForge.netから1つのダウンロードファイルで入手できます。
ただし、この2つのオープンソースプロジェクトのプロジェクト管理者であるElmar Sonnenschein(J2MEUnit)とBrunno Silva(JMUnit)は、2つのフレームワークを合体させて1つにまとめるつもりです。新しいプロジェクトはJ2MEUnitプロジェクトの下に統合されます。Sonnenscheinによると、「ユーザーベースが大きくなるので、新しいプロジェクトはSourceForgeでJ2MEUnitという名前の下にホストされることになる。我々はBrunnoのJMUnit 2.0コードベースからJ2MEUnit 2.0を作成するつもりだ」。SonnenscheinはSilvaとの最近のやりとりの中で、2.0リリースと合体後の製品の完成が今年中には間に合わないだろうと言っています。合体版が提供されるまでMEアプリケーションでの単体テストの利用を先送りにするという手もありますが、先に取り組んでみたらどうでしょうか。さらにSilvaは次のように言っています。「これらのプロジェクトでは両フレームワークの現在のユーザーのコードを無駄にしたくないので、元のコードを提供する予定だが、できるだけ減らす方向で進めている。合体したフレームワークはJMUnitとJ2MEUnitの両方の機能を持つことになるはずだ」。
単純なアプリケーション
種々の単体テストフレームワークを調べるには、テスト対象となる単純なコードが必要になります。今回は、下記の単純な変換クラスを使って、Java MEの単体テストを作成しテストすることにします。
public class DistanceConversion { public static int feetToMeters(int ft) { return (ft * 3048) / 10000; } public static int metersToFeet(int meters) { return (meters * 3281) / 1000; } public static int milesToKM(int miles) { return (miles * 1609) / 1000; } public static int kmToMiles(int km) { return (km * 6214) / 10000; } } public class TemperatureConversion { public static float fahrenheitToCelsius(float degrees) { return ((degrees - 32) / 9) * 5; } public static float celsiusToFahrenheit(float degrees) { return ((degrees * 9) / 5) + 32; } public static boolean isHotter(float degFaren, float degCel) { return ((fahrenheitToCelsius(degFaren)) - degCel) > 0; } public static boolean isCooler(float degFaren, float degCel) { return ((fahrenheitToCelsius(degFaren)) - degCel) < 0; } }
このコードではCLDC 1.1の浮動小数点基本型を使用しています。このコードをCLDC 1.0環境で実行するためには、浮動小数点基本型に代わって、下記のように整数基本型を使用する必要があります。本稿のダウンロードサンプルでは、CLDC 1.1用とCLDC 1.0用の両方のコードとテストクラスをzipファイルで提供しています。
public class DistanceConversion { public static int feetToMeters(int ft) { return (ft * 3048) / 10000; } public static int metersToFeet(int meters) { return (meters * 3281) / 1000; } public static int milesToKM(int miles) { return (miles * 1609) / 1000; } public static int kmToMiles(int km) { return (km * 6214) / 10000; } } public class TemperatureConversion { public static int fahrenheitToCelsius(int degrees) { return ((degrees - 32) / 9) * 5; } public static int celsiusToFahrenheit(int degrees) { return ((degrees * 9) / 5) + 32; } public static boolean isHotter(int degFaren, int degCel) { return ((fahrenheitToCelsius(degFaren)) - degCel) > 0; } public static boolean isCooler(int degFaren, int degCel) { return ((fahrenheitToCelsius(degFaren)) - degCel) < 0; } }