イントロダクション
前回までは、部分的にテストケースなどを作成してきましたが、テストケースの作成をするために必要なこと〜実装までを記載したいと思います。
今回は、まとめ的に、JUnitでの実装をやります。今までは、コードを書き終わったものを記載していましたが、今回はコードを書きながら記載していこうと思います。自分のやり方になりますが。。。
テストクラスの実装方法
仕様確認1
- koza.csvファイルが存在するときはファイルを読み取り、それをDataクラスに設定し、リストにして返す
- koza.csvがない場合は、システムエラーを出す
上の仕様を満たす様に、テストケースを考えます。シンプルなもので、単純に実装すれば良さそうです。
「〜そうです。」と記載したのは、実装などはやってみないとわからないこともあるのでこの様に記載しました。早速作ってみます。
テストケースを作る
まずは、単純にテストクラスを作成します。このクラス名は「テスト対象クラスの名前+Test」という形で作成します。
テストクラスと実装するクラスのセットが、そうするとわかりやすいからです。すでに実装していますが「初めから作る」という前提で記載します。
※ 「スタブ」というのは空実装、つまりメソッドの名前、引数、返り値を書いているけど中身のない状態です。
```java
/** スタブ実装 */
public void stubMethod() {
}
```
- 作成するクラス
- KozaManager(実装クラス)
- KozaManagerTest(テストクラス)
<実装クラス>
public class KozaManager {
public Listl<Data> fileRead() {
// 実装前なのでスタブにします。
}
}
<テストクラス>
public class KozaManagerTest {
@Test
public void testFileRead() {
// これもスタブ
}
}
現状は、上記の様な状態です。
そして、考えます。何を?。。。仕様の1番目「koza.csvファイルが存在するときはファイルを読み取り、それをDataクラスに設定し、リストにして返す」にはどの様なテストが必要か?を考えます。単純に考えるならば以下の様になります。
- ファイルの存在を確認する処理が想定通りに動くか?
- ファイルが存在するならば、それを読み込みデータリストを返却するか?
そして、もう1つの仕様「koza.csvがない場合は、システムエラーを出す」に関しては
- ファイルが存在しないときはシステムエラーを出し、アプリケーションを終了するか?
それぞれ、上の様なチェック(テスト)を行います。ぶっちゃけて、まずはこの単純な確認を行ってから次に「穴はないか?」を考えれば良いと思います。
なのでシンプルにその様なテストケースを作成します。
まずは、「ファイルの存在を確認する処理が想定通りに動くか?」これを確認するために実装クラスとテストクラスの両方を修正します。着手するのは1つずつなので安心ください。
ますは、テストケース(テストクラス)
<テストクラス>
public class KozaManagerTest {
@Test
public void testIsFile() {
// ファイルの存在チェック処理の確認
}
// 今はテストしないのでコメントアウト
// @Test
public void testFileRead() {
// これもスタブ
}
}
とりあえずは、ファイルの存在チェック処理を動かすためのテストケース(メソッド)を作成しました。
ここで、足りないものがあります。
- テスト対象クラス(実装するクラス)
- ファイル存在チェック処理メソッド
通常の開発だと、これらを実装してからテストしますが。先に箱(スタブ)だけ作ってしまいます。
<実装クラス>
public class KozaManager {
public boolean isFile() {
// とりあえずスタブです。ファイルが存在していることを確認する
}
public Listl<Data> fileRead() {
// 実装前なのでスタブにします。ファイルを読み込めることを確認する
}
}
そして、ちょっと考えてみると「ファイルの存在チェック処理を行うのに、ファイル名が必要だな。。。」と気がつくと思います。
ここが悩みどころ
ファイル名を(引数で)渡す処理方法と、ファイル名を固定して渡す方法のどちらもできるので「どちらの手段が汎用性、保守性共に高いだろうか?」と悩んでみます。
自分の結論は「引数でファイル名を渡す」方が良いと思ったのですが、すでに実装ているコードではコンストラクタでCSVファイルを読み込んでいるので今回は、引数なしでのメソッドにします。つまり、処理の順序的にCSVファイルを読み込んでから呼び出す処理になっているという意味です。コンストラクタの実装は下のように作っています。※設計的にビミョウな感じがします。。。
```java
public class KozaManager {
/** ファイルへの書き出しクラス */
private BufferedWriter write;
/** ファイルの読み込みクラス */
private BufferedReader read;
/** 取得(作成)するファイルクラス */
private File file;
/** コンストラクタ */
public KozaManager() {
// 操作するファイルを指定する
file = new File(FILE_PATH);
try {
write = new BufferedWriter(new FileWriter(file, true));
if (file.exists()) {
read = new BufferedReader(new FileReader(file));
}
} catch (IOException ie) {
ie.printStackTrace();
System.out.println("ファイルオープンに失敗しました。" + ie.getMessage());
System.exit(-1);
}
}
}
```
なので以下の様なコードになります。
<実装クラス>
public class KozaManager {
/** 作成するファイルが存在するかチェック */
public boolean isFile() {
// fileはフィールド変数
return file.exists();
}
public Listl<Data> fileRead() {
// 実装前なのでスタブにします。
}
}
<テストクラス>
テストの実行前に、テスト対象クラスのインスタンスを作成(newする)し、フィールド変数にセットします。
つまり、以下の順序で処理が走ります。
1. 「\@Before」アノテーションのついているメソッド
2. 「\@Test」アノテーションのついているメソッド
テストケースの数だけ「\@Before」が動きます。
public class KozaManagerTest {
@Before
public void initTest() {
target = new KozaManager();
}
@Test
public void testIsFile() {
// コンストラクタでファイルが作成されることに注意
assertTrue(target.isFile("koa.csv"));
}
// 今はテストしないのでコメントアウト
// @Test
public void testFileRead() {
// これもスタブ
}
}
実行結果は下にあります。
まとめ
「テストケースを作る=実装を行う」順序
1.作成するプログラムの仕様を確認する
- 仕様を満たすテストケースを考える
- 考えたテストケースを実装する(スタブ)※テストする内容をコメントしておくと良い
- 実行するクラス(KozaManager)を実装する
- テストケースを実行(KozaManagerTest)して、処理が正しいことを確認
クラス、メソッドの依存度が高いと右を修正した後に左を修正。。。と無限ループすることがあるのでそれぞれの処理が(なるべく)独立するように作りましょう。
でわでわ。。。
<Java関連の動画リスト>
<JUnit関連の動画リスト>