Java RPi JDKインストール 〜ラズパイにJDK(Embedded)をインストール〜

ラズパイに、JDKをインストールしようと思います。
はっきり言って
sudo apt-get install -y galternatives openjdk-8-jdkでインストールできます。

が。。。やっぱりRPiにインストールするのでちゃんと選ばないといけないような気がしました。なので調べます。

結論から言うと、JDK Embeddedをインストールします、そしてMavenのインストールについても少し触れます。

ORACLEのサイトを見る

ドキュメントのルートへのリンクはこれです

オラクルに組み込みデバイスへのJDKインストール関連のドキュメントがありました。これをみていこうと思います。

JDKインストール

JDKをインストールのページによると以下のような文言がありました。

Oracle Java SE Embeddedには、すぐに組み込みデバイスにインストールできるJREがありません。jrecreateホストコンピューターでツールを使用してカスタムJREをビルドし、そのJREを組み込みデバイスにコピーする必要があります。

ここからダウンロードできるようです。

見つけるのに一苦労しました。

JDKのダウンロードはこちらのサイトから行います。

ここは「アーカイブ」のページになっているので見つけるのが大変でした。

そして、解凍します。すると下のようなファイルが入っています。

ドキュメントによると、[JREを生成する必要がある]のでその手順も踏みます。

そして、ここのページに従い、インストールを行います。

やっぱり、インストールの確認には「ハローワールド」をやるみたいです(笑)。

Mavenのインストール

ここのサイト(英語)が叩くコマンドが書いてありとても分かり易かったです。詳細は翻訳で。。。

ここまでやれば、アプリケーションの起動準備ができたも同然です。。。がやって見ないとわからないのが世の常。。。

これから実行しようと思います。

でわでわ。。。



Eclipse IoT インストール ~WindowsでEclipse+Iot~

今回はEclipseをWindowsにインストールしてIoT開発の準備をします。IoTと一言で言っても範囲が広いのでここではBluetoothを使用するプログラミングの準備という意味でIoTという言葉を使っています。

Eclipseインストール

MergDocからダウンロードします。そして、ダウンロードしたものをC:\直下に展開します。

ZIPから展開したもの

pleiadesフォルダ

このフォルダの中にEclipse.exeがあります。

C:\pleiades\eclipse

eclipse.exe起動

とりあえずはeclipseをインストールしたので動かしたくなるのが人情、そして動かしてみた結果以下のようなエラーが出ました。

このようなエラーは、設定ファイルに関連するものなので下のファイルを確認します。

eclipse.ini

そして、ファイルの内容は以下のようになっています。

-product
org.eclipse.epp.package.jee.product
--launcher.defaultAction
openFile
--launcher.appendVmargs
-vmargs
-Dosgi.requiredJavaVersion=1.8
-XX:+UseG1GC
-XX:+UseStringDeduplication
--add-modules=ALL-SYSTEM
-Dosgi.dataAreaRequiresExplicitInit=true
-Xverify:none
-javaagent:dropins/MergeDoc/eclipse/plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar
-javaagent:lombok.jar

この設定内容をいちいち確認しているときりがないので、要点のみ見ていきます。

結論から言うとjavaが起動できていない状態です。

なので、javaコマンドを起動するためjava.exeへのパスを指定してやる必要があります。
<手法1>
環境変数を指定する:インストールしたJDKのbinに環境変数を指定します。そうすることで、パス指定などをしなくても「javaコマンド」が起動できるので上のようなエラーが出ません。
<手法2>
起動するjava.exeを指定します。別の言い方をすると起動するJVMを指定するという言い方になります。
下のような記述でeclipse.iniに追記を行うとJVM(java.exe)の指定ができます。Java8,8,9,12と切り替えて使用した場合に使える設定といえます。今回は手法2でやっていきます。

JDKのインストール

今回は、ORACLEからダウンロードします。
そして、ダウンロードしたものを起動します。

下のような画面が出てきます。がすべて「次へ」を押下しておっけです。
インストールされる場所はProgram Filesになります。

ちなみにここで変更もできます。
インストール後

そしてここのパスをコピーします。

JVMの指定

eclipse.iniにJVM設定を行います。

  1. 使用するJDKの場所を確認する
  2. 対象になるJDK\binのパスをコピーする
  3. eclipse.iniに下のように記述する
    -vm
    コピーしたパス

実際に作成したものは以下のようになりました。
市場運上に「-vm」で記述した部分が追加したところです。

-vm
C:\Program Files\Java\jdk1.8.0_231\bin
-product
org.eclipse.epp.package.jee.product
--launcher.defaultAction
openFile
--launcher.appendVmargs
-vmargs
-Dosgi.requiredJavaVersion=1.8
-XX:+UseG1GC
-XX:+UseStringDeduplication
--add-modules=ALL-SYSTEM
-Dosgi.dataAreaRequiresExplicitInit=true
-Xverify:none
-javaagent:dropins/MergeDoc/eclipse/plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar
-javaagent:lombok.jar

ワークスペースの指定

デフォルトではeclipseフォルダの隣にある「workspace」フォルダが指定されています。

ここで問題ないので、そのままワークスペースを作成します。

Eclipseを起動してみると上のような表示が見れます。
ここから、IoT開発のプラグ銀などを入れていきます。

IoTプラグインは。。。

色々試したけれどうまくいかず。。。
結局のところ、EclipseにCodename oneというプラグインをインストールしてやってみることにしました。
IoTの実装に関しては、ここで少し内容を理解してからになります。。。

IoTプラグイン


上のように「ヘルプ(help」→「新規インストール」

そして、参照するリポジトリのURLを指定します。
使用すすpluginはArduino用のプラグインとラズパイのプラグインがあるようですが、今回はシンプルにJavaMEをインストールします。

JavaME

こちらのページからSDKをダウンロードする
ダウンロードしたファイルはexeファイル、つまりはインストーラーだったのでダブルクリックでインストール可能です。

とりあえずは、このままマーケットプレイスを使用します。簡単にEclipseMarketPlaceを使用します。

今回は「Codenamwe One」をインストールしてみます。微妙な時はアンインストールすればよし。。。

Eclipseプラグイン(失敗)

オラクルのぺーじからダウンロードします。そして細かいドキュメントは左のリンク先にあります。

ダウンロードしたZIPファイルを展開すると下のようなものが入っています。

Eclipseを起動したらヘルプをクリックします。

そして、「新規ソフトウェアのインストール」をクリック、してアーカイブを選択してダウンロードしたZIPファイルを指定します。

ここにチェックを入れて「次へ」をクリックしてやると検証が始まります。

いろいろやったのでなんか出ましたが、そのまま続けます。(古いものを削除する内容でしたので。。。)

さらに何かでましたので、原因を探ります。そして、原因ははっきりとわかりませんが、MobileTook Kitがあれば問題なさそうです。

ちょっと長くなったので続きは次回にします。

そのほかのもののインストール

  1. サクラエディタ
  2. 画像キャプチャ(Win Shot)

とりあえずはこんなもんでしょうか?

でわでわ。。。

Javaスキル36房〜第34房: 四則計算を行う〜

プログラミングの入り口「ハローワールド」を突破したら、今度は「四則計算」を行います。
パソコンはなんだかんだ言っても「計算機」なので計算が基本になります。

PCは計算機

単純に画面上に絵を描く、何かの入力用のテキストボックスを配置するなどは全て「座標計算」が土台(基本)になっています。
最近では「当たり前」になっているものですが、先人の努力があり現代のような「簡単にできるプログラム」が存在ます。
興味のある人は「低レベルAPI」について調べて見ると良いかもしれません。

低レベルAPIはなんでもできる

具体的にはC言語とかアセンブリなどの言語を指すことが多いですが、Javaも低レベルAPIがあり、C言語と同じレベルでコードが書けたりします。そして、アプリケーションの土台部分を作っているのが「低レベルAPI」なので大体のことは可能です。
その代わり、実装しなくてはいけないプログラムの量は多くなります。

四則計算を行う

Javaでの四則計算は電卓などと同じように「+」や「-」を使用します。ちょっと違う部分は掛け算と割り算に使用する「*」や「/」です。それぞれ以下のように書きます。

//足し算
int plus = 1 + 1;
int minus = 1 -1;
int malutiplication = 1 * 1;
/* 割り算は小数点がありうるので小数点用のデータ型を
 * 使用します。
 * ちなみに「int」で宣言すると整数部分しか結果が得られません。
  */
double division = 1 / 1;

計算プログラム

初めてプログラムに触る人向けに行っているので計算をするための土台部分をサンプルコードとして下に示します。

// プログラムを起動すると入力を待ちます。
Scanner input = new Scanner(System.in);
String inStr = input.nextLine();

// 入力した値を表示します。
System.out.println(inStr);

<プログラムの起動イメージ>

そして、上に示したように入力した値を「文字列」から「数値」に変換して計算を行います。
その計算結果をコンソールに表示するようなプログラムを作成してください。

ヒント

上に示した、プログラムを拡張して行うとやりやすいと思います。ちなみにサンプルコードはGithubにアップロードしてあります。

行うべき処理

  1. 標準入力を受け取るString inStr = input.nextLine();
  2. 受け取った値を数値に変換する(やり方を自分で見つけましょう、ネット上にいくらでもサンプルがあります)

参考サイト

  1. 開発ツールのインストール方法
  2. 四則計算のやり方
  3. 標準入力から四則計算を行う
  4. Eclipse インストール方法~windowsへのインストール~


Java Mid Basic 〜チェッカークラスを作る〜

オブジェクト指向の考え方では1クラス1機能です。前回作成した、数秘術→社会人基礎力の数値を算出するクラス内に入力チェック処理を作るとあの処理、この処理…と面倒なので、チェッカークラスを作成します。

そして、staticの使い方に関しても触れます。

チェッカークラスは、チェック処理を担当するクラスです。

コマンドクラスから呼び出す

コマンドクラスはインターフェースを使用してCommandIFを実装したクラスであればなんでも良い様に作成しました。

今度は、入力チェック用のクラスを作成します。ここで大切なのは「どの様に使うか?」です。

今回は、シンプルにチェッカーユーティリティクラスを作成します。クラス名は「CheckwrUtil」にします。

使い方は以下の様に静的呼び出しで行います。理由は、インスタンス化する意味がないからこの様な形で実装します。

CheckerUtil.isMandatory("文字列");

クラスの実装は下の様になります。

public class ChckerUtil {
    public static boolean  isMandatory(String inut) {
        // チェック処理
    }
}

そして、必須入力チェックなので、入力の有無を判定し、TrueかFalseを返却するように実装してやればオッケー。

具体的に

コマンドクラスでの実装サンプルです。

public class HelloCommand implements CommandIF {

	/* (non-Javadoc)
	 * @see jp.zenryoku.sample.lv3.refactor.CommandIF#execute()
	 */
	@Override
	public void execute() {
		// TODO Auto-generated method stub
		System.out.println("名前を入力してください");
      Scanner input = new Scanner(System.in);
      String inStr = input.nextLine();

      if (CheckerUtil.isMandatory(inStr)) {
         // 未入力時の処理
	   } else {
         // 入力ありの時
      }
   }
}

executeメソッドで、入力時のチェック処理を行います。

とりあえずは、こんな感じの実装で使用することを想定して作りました。

ポイント

通常は、newしてから使用するメンバーメソッドを作るのですが、この処理には、インスタンスを作る必要がないので、staticをつけた静的メソッドに行くしています。

詳細

インスタンスを作る必要性に関して「通常」と言う言葉を使いましたが、何かしらの処理を行うのに、準備が必要な時、処理するデータを1クラスに1つ持たせたい場合などにはインスタンスが必要です。

例えば、DBにアクセスする時には、DBサーバーへのコネクションを取得、保持する必要があり、アクセス(データを取得する操作を複数人が行う想定の場合にはstatic(静的)クラスやメソッドは使えません。何故かと言うとstaticをつけると、メソッドの場合は、インスタンス1つにつき1つのクラスにしか影響しなかったのが全てのインスタンスに影響します。

具体的には

リストにデータを設定する場合、リストの中身のデータクラスがstaticだと、リストの中身が全部同じになります。

逆にstaticでない場合は、それぞれのクラスに別々のデータが設定されます。


Java はじめて31 〜JUnitでのテスト駆動型開発8: 処理をつなげたテスト〜

今回は、今まで作成した1処理を「機能」として動かせることを確認するテストケースを作成します。
通常のやり方では、1.実装、2.テストと分けた場合の「2.テスト」にあたります。

機能テスト

仕様は以下の通りです。

仕様
1. ファイルを操作するためのオブジェクトを作成する
2. ファイルにデータをCSV形式で書き出す。
3. ファイルが存在するのであれば、それを読み込みデータを保持する

今までの実装、テストで行くと「3」が実装したものに相当します。なので、これをテストケースで確認する様にします。
現状では、スタブのままなので未実装になっています。
なのでテストケースを先に作成します。
ちなみに、最終的なコードはGithubにあります。

  1. KozaManager.java
  2. KozaManagerTest.java
@Test
public void testFileRead() {
    if (target.isFile() == false) {
        fail("ファイルが存在していません");
    }
    List<Data> dataList = target.readFile();
    // データは1件
    assertEquals(1, dataList.size());
    // ファイルにあるデータ
    Data data = dataList.get(0);
    assertEquals("test", data.getName());
    assertEquals("passwd", data.getPassword());
}

とりあえずで、上の様に作成しました。初めにファイルの存在チェック処理を確認して、ファイルが存在しないならテスト失敗。
次にファイルの読み込みを行い、そのデータを取得しデータが想定通りのものが入っているか確認します。

余談

テストを複数回行っていたので「koza.csv」にデータが複数作成されていました。ヘッダー付きなのでこれはダメなテストになってしまうので、ちょっと修正します。
<修正1>
「ファイルが存在するときにはファイル作成をしない」が抜けていたのでこれを追加します。

// ファイルそ新規で作成するとき
if (file.exists() == false) {
    // ヘッダー部分の出力
    build.append(this.createCSVHeader());
    // ファイル書き込み処理
    write.write(build.toString());
    write.newLine();
}

そして、修正後のメソッドは以下の様になります。

public void dataOutput(Data data) throws IOException {
    if (file.canWrite() == false) {
        throw new IOException("ファイルの書き込みができません: " + file.getAbsolutePath());
    }
    // おおよそのデータサイズを指定すると余計なメモリを使用しなくて済む
    StringBuilder build = new StringBuilder(50);
    // ファイルそ新規で作成するとき
    if (file.exists() == false) {
        // ヘッダー部分の出力
        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 testFileRead() {
    if (target.isFile() == false) {
        fail("ファイルが存在していません");
    }
    List dataList = target.readFile();
    // データは1件
    assertEquals(1, dataList.size());
    // ファイルにあるデータ
    Data data = dataList.get(0);
    assertEquals("test", data.getName());
    assertEquals("passwd", data.getPassword());
}

取得したデータのサイズ(ファイルの行数)は1の想定ですが、テストを実行するたびにデータが追加されるので、テストが毎回同じ結果になりません。

これは良くない

なので、ここもちょっと修正します。

// データは0件ではない
assertNotEquals(0, dataList.size());

こんな感じで修正しました。そして、複数件あっても作成されるデータは同じなので初めの一件目が想定通りの値かどうか確認します。ここでちょっと注意「初めの1行目はヘッダー行なのでデータ部ではないですが、取得したときはリストの1番目に設定されています。なので以下の様に修正します。

public void testFileRead() {
    if (target.isFile() == false) {
        fail("ファイルが存在していません");
    }
    List dataList = target.readFile();
    // データは0件ではない
    assertNotEquals(0, dataList.size());
    // ファイルにあるデータは2行目から
    Data data = dataList.get(1);
    assertEquals("test", data.getName());
    assertEquals("passwd", data.getPassword());
}

こんな感じでファイル読み込みの一連の機能をテストしました。これと同様に、他ものもテストしました。わかりづらいですが、上のキャプチャにテストケースが、4つあります。
ファイル作成、ファイル存在チェックの処理の他は読み込みと書き込みをテストしています。

ちょっと締まりが悪いですが。今回はこれまでにします。

でわでわ。。。



Java はじめて30 〜JUnitでのテスト駆動型開発7: テストクラスの実装方法〜

今回は、JUnitでの実装をやります。今までは、コードを書き終わったものを記載していましたが、今回はコードを書きながら記載していこうと思います。自分のやり方になりますが。。。

テストクラスの実装方法

仕様確認1

  1. koza.csvファイルが存在するときはファイルを読み取り、それをDataクラスに設定し、リストにして返す
  2. koza.csvがない場合は、システムエラーを出す

上の仕様を満たす様に、テストケースを考えます。シンプルなもので、単純に実装すれば良さそうです。
「〜そうです。」と記載したのは、実装などはやってみないとわからないこともあるのでこの様に記載しました。早速作ってみます。

テストケースを作る

まずは、単純にテストクラスを作成します。このクラス名は「テスト対象クラスの名前+Test」という形で作成します。
テストクラスと実装するクラスのセットが、そうするとわかりやすいからです。すでに実装していますが「初めから作る」というていで記載します。

作成するクラス
KozaManager(実装クラス)
KozaManagerTest(テストクラス)

<実装クラス>

public class KozaManager {

    public Listl fileRead() {
        // 実装前なのでスタブにします。
    }

} 

<テストクラス>

public class KozaManagerTest {
    @Test
    public void testFileRead() {
        // これもスタブ
    }
}

現状は、上記の様な状態です。
そして、考えます。何を?。。。仕様の1番目「koza.csvファイルが存在するときはファイルを読み取り、それをDataクラスに設定し、リストにして返す」にはどの様なテストが必要か?を考えます。単純に考えるならば以下の様になります。

  1. ファイルの存在を確認する処理が想定通りに動くか?
  2. ファイルが存在するならば、それを読み込みデータリストを返却するか?

そして、もう1つの仕様「koza.csvがない場合は、システムエラーを出す」に関しては

  1. ファイルが存在しないときはシステムエラーを出し、アプリケーションを終了するか?

それぞれ、上の様なチェック(テスト)を行います。ぶっちゃけて、まずはこの単純な確認を行ってから次に「穴はないか?」を考えれば良いと思います。

なのでシンプルにその様なテストケースを作成します。
まずは。「ファイルの存在を確認する処理が想定通りに動くか?」これを確認するために実装クラスとテストクラスの両方を修正します。着手するのは1つずつなので安心ください。
ますは、テストケース(テストクラス)
<テストクラス>

public class KozaManagerTest {
    @Test
    public void testIsFile() {
        // ファイルの存在チェック処理の確認
    }
    // 今はテストしないのでコメントアウト
    // @Test
    public void testFileRead() {
        // これもスタブ
    }
}

とりあえずは、ファイルの存在チェック処理を動かすためのテストケース(メソッド)を作成しました。
ここで、足りないものがあります。

  1. テスト対象クラス(実装するクラス)
  2. ファイル存在チェック処理メソッド

通常の開発だと、これらを実装してからテストしますが。先に箱(スタブ)だけ作ってしまいます。
<実装クラス>

public class KozaManager {
    public boolean isFile() {
        // とりあえずスタブです。
    }
    public Listl fileRead() {
        // 実装前なのでスタブにします。
    }

} 

そして、ちょっと考えてみると「ファイルの存在チェック処理を行うのに、ファイル名が必要だな。。。」と気がつくと思います。

ここが悩みどころ

ファイル名を(引数で)渡す処理方法と、ファイル名を固定して渡す方法のどちらもできるので「どちらの手段が汎用性、保守性共に高いだろうか?」と悩んでみます。
自分の結論は「引数でファイル名を渡す」方が良いと思ったのですが、すでに実装ている部分ではコンストラクタで生成しているので今回は、引数なしでのメソッドにします。なので以下の様なコードになります。
<実装クラス>

public class KozaManager {
    /** 作成するファイルが存在するかチェック */
    public boolean isFile() {
        // fileはフィールド変数
        return file.exists();
    }
    public Listl fileRead() {
        // 実装前なのでスタブにします。
    }
} 

<テストクラス>

public class KozaManagerTest {
    @Before
    public void initTest() {
        target = new KozaManager();
    }

    @Test
    public void testIsFile() {
        // コンストラクタでファイルが作成されることに注意
        assertTrue(target.isFile("koa.csv"));
    }

    // 今はテストしないのでコメントアウト
    // @Test
    public void testFileRead() {
        // これもスタブ
    }
}

実行結果は下にあります。

でわでわ。。。



Java はじめて28 〜JUnitでのテスト駆動型開発5: ファイル出力の実装〜

今回は、ファイル出力処理を実装します。つまりテストケースの作成と実行を行います。

ファイル出力の実装

ファイル出力と言っても、ファイルの作成自体はすでに実装済みなのでデータをファイルに書き込む処理の実装ということになります。
<前回までにできていること>

  1. ファイル作成(koza.csv)
  2. ファイルの存在チェック(KozaManager#isFile)

ここまでが実装したものになります。ちなみにテストコードは下の様になります。

public class KozaManagerTest {
    /** テスト対象クラス */
    private KozaManager target;

    /**
     *  テストの初期化 
     *  各テスト実行前に起動する
     */
    @Before
    public void initClass() {
        target = new KozaManager();
    }

    /**
     * コンストラクタが起動したかどうかを確認する
     * テストケース
     */
    @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());
}

「あー、これはファイルの中身がなくて当然だなぁ」と気がつきます(笑)
上のコードでは、出力用文字列作成用のオブエジェクトを作成しているだけです。

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」クラスのインスタンスが解放されるときに各フィールドの解放を行います。(デストラクタに実装)

/** デストラクタ */
@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 はじめて27 〜JUnitでのテスト駆動型開発4: 追加修正と実装〜

今回は、前回同様に「JUnitでのテストケースから、テストをクリアできる様に実態クラスを作成します。」が最終的にファイルを出力する前に、出力したファイルの存在チェック処理を作る必要があるので、そちらの処理(テストケース)を先に作成します。

追加修正

前回までに作成したものをまとめると以下の様になります。
<口座管理処理の要件>

  1. ファイルを操作するためのオブジェクトを作成する
  2. ファイルにデータをCSV形式で書き出す。
  3. ファイルが存在するのであれば、それを読み込みデータを保持する

<実装済み>
1のオブジェクト作成のみ

<テストケース>
ファイル出力のテストケースとKozaManagerクラス(本体)の実装時に、以下の不足する実装(処理)があることに気がつく

  1. 出力するCSVのヘッダー部分の作成処理
  2. デストラクタ
  3. 作成したファイルの存在確認
  4. ファイルの内容確認

上記の様なことが抜けていたので、作成するテストケースも作成順序が変わってきてしまいました。元の設計に抜けがあった様です。つまり、以下の様な順序で作成することになります。

  1. ファイルの存在チェック処理
  2. ファイルにCSVデータを出力する処理
  3. ファイルの内容確認処理
  4. ファイル出力の処理(上の1-3を全て通して行う確認)

余談ですが、テストケースを作成することで「不足ケース」を見つけることができます。当然不足があれば、テストは想定通りには動きません、プログラムですから「気を利かして。。。」なんてことはやってくれません(笑)

ファイル存在チェック

はっきり言って1行で終わります。
コンストラクタで参照(作成)するファイルオブジェクトは作成しているのでそれを使用します。
なので、もともと下の様なコンストラクタを使っていましたが、修正します。
<元のコード>

public class KozaManager {
    /** ファイルへの書き出しクラス */
    private BufferedWriter write;
    /** ファイルの読み込みクラス */
    private BufferedReader read;

    /** コンストラクタ */
    public KozaManager() {
        // 操作するファイルを指定する
        File file = new File("resources/koza.csv");
        try {
            write = new BufferedWriter(new FileWriter(file));
            if (file.exists()) {
                read = new BufferedReader(new FileReader(file));
            }
        } catch (IOException ie) {
            ie.printStackTrace();
            System.out.println("ファイルオープンに失敗しました。" + ie.getMessage());
            System.exit(-1);
        }
    }

    /** デストラクタ */
    @Override
    protected void finalize() throws Throwable {
        write = null;
        read = null;
    }

    /**
     * データクラスを受け取り、CSVファイルを出力する(書き出しを行う)
     * @param data コーダー銀行のユーザー情報
     */
    public void dataOutput(Data data) {
    }
}

<修正後のコード>
・フィールドを追加します。
・コンストラクタの「file」をフィールド変数のFIleオブジェクトに変更します。

public KozaManager() {
    // 操作するファイルを指定する
    file = new File(FILE_PATH);
    try {
        write = new BufferedWriter(new FileWriter(file));
        if (file.exists()) {
            read = new BufferedReader(new FileReader(file));
        }
    } catch (IOException ie) {
        ie.printStackTrace();
        System.out.println("ファイルオープンに失敗しました。" + ie.getMessage());
        System.exit(-1);
    }
}

本来であれば、ファイルの作成はプログラムからやるのですが、順番的にファイルの存在チェックを先にやるので手で作成します。
以下の様なファイルです。

名前, パスワード
テスト太郎, 1234
テスト花子, 2345
テストたくのじ, 3456

これを「koza.csv」と名前をつけてEclipseのresourcesフォルダの直下に配置(作成)します。。。
が、以前起動したテスト(コンストラクタのテスト)の実行時にファイルが作成されていたので(ファイルの中身は空)、テストを起動した時点でファイルが存在することになります。
なので、テストケースとしては以下の様なものになります。

public void testIsFile() {
    assertTrue(target.isFile());
}

これも1行で終わりました(笑)

しかし、これでテストを実行しても上のコードはテストとして実行されません、なぜなら「@Test」がついていないからです。
なので以下の様にテストクラスを修正します。

public class KozaManagerTest {
    /** テスト対象クラス */
    private KozaManager target;

    /**
     *  テストの初期化 
     *  各テスト実行前に起動する
     */
    @Before
    public void initClass() {
        target = new KozaManager();
    }

    /**
     * コンストラクタが起動したかどうかを確認する
     * テストケース
     */
    @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」がついていないところです。つまりこのテストケースは実行されません。では動かしてみます。

とりあえずはテスト成功の様です。ここでファイルの存在チェックができたので、次はファイル出力のテスト実装に入ろうと思います、

でわでわ。。。



Java はじめて26 〜JUnitでのテスト駆動型開発3: クラスの実装〜

今回は、JUnitでのテストケースから、テストをクリアできる様に実態クラスを作成します。
つまり、JUnitでテストケースを要件を満たす様に作成し、テスト実行後に緑色の表示がされる様にKozaManagerのメソッドを作成します。
ちなみに、今回の実装する時の要件は以下の様になっています。

  1. ファイルを操作するためのオブジェクトを作成する
  2. ファイルにデータをCSV形式で書き出す。
  3. ファイルが存在するのであれば、それを読み込みデータを保持する

オブジェクト指向での実装を行うので、どの様に考えれば良いか?の指針になれば良いと思います。

今までの概要

前回作成したのは、上の要件のうちの1(コンストラクタで実装)だけです、これはテストを実行する準備段階でもあるので、テストケースとしては「インスタンスがtargetに設定されていること」を確認するテストケースを作成しました。

public class KozaManager {
    /** ファイルへの書き出しクラス */
    private BufferedWriter write;
    /** ファイルの読み込みクラス */
    private BufferedReader read;

    /** コンストラクタ */
    public KozaManager() {
        // 操作するファイルを指定する
        File file = new File("resources/koza.csv");
        try {
            write = new BufferedWriter(new FileWriter(file));
            if (file.exists()) {
                read = new BufferedReader(new FileReader(file));
            }
        } catch (IOException ie) {
            ie.printStackTrace();
            System.out.println("ファイルオープンに失敗しました。" + ie.getMessage());
            System.exit(-1);
        }
    }

    /** デストラクタ */
    @Override
    protected void finalize() throws Throwable {
        write = null;
        read = null;
    }

    /**
     * データクラスを受け取り、CSVファイルを出力する(書き出しを行う)
     * @param data コーダー銀行のユーザー情報
     */
    public void dataOutput(Data data) {
    }
}

コンストラクタと、デストラクタを実装しています。
コンストラクタは、ファイル操作に必要なクラスのインスタンスを作成します。
デストラクタは作詞したインスタンスを解放します。

現状のテストコード

package jp.zenryoku.apps.atm.manage;

import static org.junit.Assert.assertNotNull;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import jp.zenryoku.apps.atm.data.Data;

/**
 * @author takunoji
 *
 * 2019/09/25
 */
public class KozaManagerTest {
    /** テスト対象クラス */
    private KozaManager target;

    /**
     *  テストの初期化 
     *  各テスト実行前に起動する
     */
    @Before
    public void initClass() {
        target = new KozaManager();
    }

    /**
     * コンストラクタが起動したかどうかを確認する
     * テストケース
     */
    @Test
    public void testIsFile() {
        assertNotNull(target);
    }

    /**
     * ファイルにデータを出力し保存するテストケースです。
     */
    public void testFileCeate() {
    }
}

テストケース作成

今回の目玉になります、現状はから実装になっている「testFileCreate()」の実装を行いますが、これはテスト対象クラス(本来作成するクラス)がどう動けば良いか?を考えて作成する必要があります。

どう動けば良いか?

口座情報管理クラスで入力されたデータをファイルに出力する必要があるので単純に「データを受けて、それをCSVに変換してファイル出力する」処理を行うのがこのメソッドの処理になります。
まとめると以下の様になります。

  1. 入力としてDataクラス(口座情報クラス)を受ける
  2. 出力として、CSVファイルを出力する

ここで、KozaManagerクラスの他に「Dataクラス」が必要であることがわかります。これは前回作成しております
そして、CSVファイルを出力するのに、ヘッダー部分のテンプレート(CSVファイルのヘッダは固定値です)を出力する必要がありますので、これをKozaManagerに追加します。
現状では、以下の様なコードになります。

/**
 * 出力するCSVのヘッダー部分を作成する。
 * @return CSVヘッダーの文字列(カンマ区切り)
 */
private String createCSVHeader() {
    return "名前, パスワード";
}

/**
 * データクラスを受け取り、CSVファイルを出力する(書き出しを行う)
 * @param data コーダー銀行のユーザー情報
 */
public void dataOutput(Data data) throws IOException {
    StringBuilder build = new StringBuilder();
    build.append(this.createCSVHeader());
    build.append(data.getName() + ",");
    build.append(data.getPassword());
}

しかし、ここで注意したいのがCSVデータは大雑把に以下の様な形式になります。そして、サンプルデータを記載しておきます。
<形式>
1行目: ヘッダー
2行目以降: データ

名前, パスワード
テスト太郎, 1234
テスト花子, 2345
テストたくのじ, 3456

頭をひねる

ここで、元のクラスに色々と修正を加える必要が出てきました。追加する修正は以下のものです。

  1. ファイル出力する時のパス指定をハードコーディング(File file = new File("resources/koza.csv");)しているのを定数としてクラスに保持したい
  2. ファイルが出力できたか?のファイル存在チェックメソッドが欲しい

これらの修正を加えるのに、テストケースで「ファイルにデータをCSV形式で書き出す。」という処理は最後になります。
上の処理を確認するのにファイルの存在チェックなどが必要なので先にそちらの処理を作成する必要があるからです。

先にファイル存在チェック処理を作る

これをやる必要が出てきました。
これは次回やろうと思います。

でわでわ。。。



Java はじめて25 〜JUnitでのテスト駆動型開発2: テストケース作成〜

今回は、コンストラクタがエラーもなく正常に動いたかどうかの確認を行うところから始めます。

前回の復習

JUnitがなんなのか?に関して記載しました。
そして、このフレームワークはテスト(作成したコード)を実行してその動きが想定通りに動いたかどうか?を「assert」を使用して確認できるものです。

動かすためのポイント
junit.jarがビルドパスに設定されている

大まかに以下の様な形で実装します。

public class KozaManagerTest {
    /** テスト対象クラス */
    private KozaManager target;

    /**
     *  テストの初期化 
     *  各テスト実行前に起動する
     */
    @Before
    public void initClass() {
        target = new KozaManager();
    }

    /**
     * コンストラクタが起動したかどうかを確認する
     * テストケース
     */
    @Test
    public void testIsFile() {
        assertNotNull(target);
    }
}

今回のテストケースを実装していますが、ポイントとしては、以下の点が挙げられます。

  1. @Beforeは書くテストケース(メソッド)が起動する前に動く
  2. @Testはテストケース(メソッド)に付ける

上のコードでは、コンストラクタが起動してフィールド変数「target」にインスタンスが設定されているかどうか?を判定します。targetにインスタンスが設定されていない=コンストラクタが起動している最中に例外(Exception)が発生して@Befireのついているメソッドの処理target = new KozaManager()の処理が終わらないうちに(targetに値が設定されないうちに)例外を受け取る処理が動くためです。

具体的に

下の動画を見てもらうとわかると思います。

この処理は、コンストラクタで例外を発生させています。

public KozaManager() {
    // 操作するファイルを指定する
    File file = new File("resources/koza.csv");
    try {
        write = new BufferedWriter(new FileWriter(file));
        if (file.exists()) {
            read = new BufferedReader(new FileReader(file));
            throw new IOException("テスト例外");
        }
    } catch (IOException ie) {
        ie.printStackTrace();
        System.out.println("ファイルオープンに失敗しました。" + ie.getMessage());
        System.exit(-1);
    }
}

ここでのポイントは例外が発生した後にアプリ(メインメソッドの処理)を強制終了しているのでJUnitの緑色とか茶色(エラー時)が表示されません。

補足

ちなみに、コードを実行した後にガベージコレクションでフィールド変数などが解放されるので実装しなくても大丈夫なのですが、テストケースでなんどもKozaMangerクラスのインスタンスを生成するのでC言語的にいうところの「デストラクタ」(コンストラクタの逆=インスタンスを削除する時)が動く時にフィールド変数を解放します。この処理を追加します。

/** デストラクタ */
@Override
protected void finalize() throws Throwable {
    write = null;
    read = null;
}

こうすることで、メモリに余計なもの(オブジェクト)を保存しないので、処理が快適に動きます。

テストケースの実装

ここからが本当のテスト駆動型開発です、現状はコンストラクタを作成していますが、説明のため。。。言い訳くさい。。。

とりあえず、以下の様に考えます。

「このクラスで要件を満たすために何をしたら良い?」

自分の答えは以下の通りです。

  1. ファイルを操作するためのオブジェクトを作成する
  2. ファイルにデータをCSV形式で書き出す。
  3. ファイルが存在するのであれば、それを読み込みデータを保持する

項目1はコンストラクタで実現しているので。今回は2のファイルにCSVデータを書き出す。を実装しようと思います。
設計としては以下の様にします。
<設計>

  1. ファイルの書き出しを行うメソッドはdataOutputという名前にする
  2. 書き出しを行うときはCSV形式で出力する
  3. データを受け取るときは「Dataクラス」で受け取る

この設計から行くと作成するメソッドは「dataOutput」で引数は「Dataクラス」になります。
そして、返却値は「なし=void」です。

なので、テストケースとしては以下の様なコードになります。

/**
 * ファイルにデータを出力し保存するテストケースです。
 */
public void testFileCeate() {
    Data data = new Data("名前", "パスワード");
    target.dataOutput(data);
}

この様に実装しましたが、「Data」クラスも「dataOutput」メソッドも存在しないのでエラーになります。
なのでこれを「スタブ=空実装(名前だけ、形)」で作成します。そして、作成するDataクラスは以下の様な形で実装します。
これはデータクラスで、内容はフィールドと「Getter(ゲッター)」と「Setter(セッター)」のみになります。

public class Data {
    /** ユーザー名 */
    private String name;
    /** パスワード */
    private String password;
    /**
     * @return the name
     */
    public String getName() {
        return name;
    }
    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * @return the password
     */
    public String getPassword() {
        return password;
    }
    /**
     * @param password the password to set
     */
    public void setPassword(String password) {
        this.password = password;
    }
}

そして、今回作成するメソッドをスタブで作成します。
さらに、データクラスをインポートします。
import jp.zenryoku.apps.atm.data.Data;

<スタブメソッド>

/**
 * データクラスを受け取り、CSVファイルを出力する(書き出しを行う)
 * @param data コーダー銀行のユーザー情報
 */
public void dataOutput(Data data) {
}

次回は、このスタブで要件を満たすことができるかどうか考え、必要な実装を行いたいと思います。

でわでわ。。。