イントロダクション
前回は実装するべきクラスの実装を行いました。しかし、ファイル入出力の部分は手付かずだったので、その部分を実装しようと思います。
<JUNITの使い方>
今回は、ファイル出力処理を実装します。つまりテストケースの作成と実行を行います。記事の下に動画があるので、参考にどうぞ、
ファイル出力の実装
ファイル出力と言っても、ファイルの作成自体はすでに実装済みなのでデータをファイルに書き込む処理の実装ということになります。
<前回までにできていること>
- ファイル作成(koza.csv)
- ファイルの存在チェック(KozaManager#isFile)
補足として、ファイルの作成 => new File()
、ファイルへの書き込み => Writer.write()
ここまでが実装したものになります。ちなみにテストコードは下の様になります。
public class KozaManagerTest {
/** テスト対象クラス */
private KozaManager target;
/**
* テストの初期化
* 各テスト実行前に起動する
*/
@Before
public void initClass() {
target = new KozaManager();
}
/**
* テスト対象クラスのメモリ開放
*/
@After
public void terminate() {
target.finalize();
}
/**
* コンストラクタが起動したかどうかを確認する
* テストケース
*/
@Test
public void testIsInstance() {
assertNotNull(target);
}
/**
* ファイルの存在チェック処理の確認
*/
@Test
public void testIsFile() {
assertTrue(target.isFile());
}
/**
* ファイルにデータを出力し保存するテストケースです。
*/
public void testFileCeate() {
Data data = new Data("名前", "パスワード");
try {
target.dataOutput(data);
} catch(IOException ie) {
ie.printStackTrace();
fail();
}
}
}
ここでの注意点は、「@Test」のついていないテストケース(メソッド)がファイル出力のテストケースになります。
なので、今回はこのコードを実行しようと思います。
早速「@Test」を上のテストケースにつけます。
/**
* ファイルにデータを出力し保存するテストケースです。
*/
@Test
public void testFileCeate() {
Data data = new Data("名前", "パスワード");
try {
target.dataOutput(data);
} catch(IOException ie) {
ie.printStackTrace();
fail();
}
}
想定通りにいかない原因
製造中につきものの「動かない。。。」にあたります。
本当であれば、テストコードの中でファイルの中身を確認するのですが、ファイル出力後のテストケースも考えていないので、とりあえず。。。と言ったところです。
さて、想定通りにいかない原因を考えます。というか探します。本体のコードを見直してみると。。。
public void dataOutput(Data data) throws IOException {
// おおよそのデータサイズを指定すると余計なメモリを使用しなくて済む
StringBuilder build = new StringBuilder(50);
// ヘッダー部分の出力
build.append(this.createCSVHeader());
// ファイル出力処理
write.append(build.toString());
// StringBuilderのクリア
build.setLength(0);
// データ部分の出力
build.append(data.getName() + ",");
build.append(data.getPassword());
}
「あー、これはファイルの中身がなくて当然だなぁ」と気がつきます(笑)
上のコードでは、出力用文字列作成用のオブエジェクト(StringBuilder)を作成しているだけです。下のようにStringBuilderに文字列を追加しないと出力するべきデータがない状態になります。
build.append(data.getName() + ",");
build.append(data.getPassword());
なので修正します。
public void dataOutput(Data data) throws IOException {
// おおよそのデータサイズを指定すると余計なメモリを使用しなくて済む
StringBuilder build = new StringBuilder(50);
// ヘッダー部分の出力
build.append(this.createCSVHeader());
// ファイル書き込み処理
write.write(build.toString());
// StringBuilderのクリア
build.setLength(0);
// データ部分の書き込み
build.append(data.getName() + ",");
build.append(data.getPassword());
write.write(build.toString());
}
そして、ファイルに出力したらファイルの入出力で使用しているReaderとWriter、そのほかのフィールド変数の後始末をする必要があります。でないとメモリ上にインスタンスが残ってしまいます。Javaの場合はガベージコレクションで最終的にメモリは解放されますが、ここでは明示的に「KozaManger」クラスのインスタンスが解放されるときに各フィールドの解放を行います。(デストラクタに実装)※finalize()を呼び出しましょう。
/** デストラクタ */
@Override
protected void finalize() throws Throwable {
// フィールド変数の後始末
file = null;
read.close();
write.close();
write = null;
read = null;
}
それでは、改めてテストを実行してみます。しかし想定通りに行きませんでした。。。
ファイルは出力しているのですが、肝心の中身が出力されていない状態でした。
原因としては「ファイルを閉じていない」と「ファイルを開くときに追記モードで開いていない」ことでした。
そして、最終的には以下の様なコードになりました。
public void dataOutput(Data data) throws IOException {
if (file.canWrite() == false) {
throw new IOException("ファイルの書き込みができません: " + file.getAbsolutePath());
}
// おおよそのデータサイズを指定すると余計なメモリを使用しなくて済む
StringBuilder build = new StringBuilder(50);
// ヘッダー部分の出力
build.append(this.createCSVHeader());
// ファイル書き込み処理
write.write(build.toString());
write.newLine();
// StringBuilderのクリア
build.setLength(0);
// データ部分の書き込み
build.append(data.getName() + ",");
build.append(data.getPassword());
write.write(build.toString());
write.newLine();
write.close();
}
というわけで、テストをしながら実装するのでやりやすい(色々と試しやすい)実装方法だと思います。
ちなみにテストケースは、色々と試したので少々変わっています。
@Test
public void testFileCeate() {
Data data = new Data("test", "passwd");
try {
target.dataOutput(data);
} catch(IOException ie) {
ie.printStackTrace();
fail("ファイル入出力に問題があります。");
} catch(Exception e) {
e.printStackTrace();
fail("想定外のエラーが起きました。");
}
}
そして、本体のクラス(メソッド)も同様に修正が入っております。最終的に以下の様な実装になりました。
public void dataOutput(Data data) throws IOException {
if (file.canWrite() == false) {
throw new IOException("ファイルの書き込みができません: " + file.getAbsolutePath());
}
// おおよそのデータサイズを指定すると余計なメモリを使用しなくて済む
StringBuilder build = new StringBuilder(50);
// ヘッダー部分の出力
build.append(this.createCSVHeader());
// ファイル書き込み処理
write.write(build.toString());
write.newLine();
// StringBuilderのクリア
build.setLength(0);
// データ部分の書き込み
build.append(data.getName() + ",");
build.append(data.getPassword());
write.write(build.toString());
write.newLine();
write.close();
}
後、ファイルを開くときに追加書き込みモードで開く必要があるので。。。(今回の実装ではデータの保存を行うのでフィールドで保持する必要があるため)コンストラクタの修正も行いました。
// 修正前のコード
write = new BufferedWriter(new FileWriter(file));
// 修正後のコード
write = new BufferedWriter(new FileWriter(file, true));
実行結果は以下の通りです。
次回は、各処理を(ファイル存在チェック〜ファイル出力)テストします。最終的な確認のコードです。
でわでわ。。。
<Java関連の動画リスト>
<JUnit関連の動画リスト>