JUnit4でのシリアライズ／デシリアライズ検証を楽チンにする！

[『Serializable実装クラスのテストについて考える』シリーズ](http://qiita.com/nmby/items/6ffc244537087438dcfd) では Serializable 実装クラスに対するテストについて取り上げたものの、シリアライズ／デシリアライズのテストって、try/catch だの IOException だの Input/OutputStream だの、**なんだか面倒**なんですよねぇ...

**だったら作れば良いじゃない！**
というわけで、テストを楽チンにするライブラリを**作りました！**

具体的には、[『JUnit4での例外テストを楽チンにする！』](http://qiita.com/nmby/items/4911e8c36088139aa0ba)で紹介した [こちらのライブラリ（GitHub）](https://github.com/nmby/jUtaime) に機能を追加しています。

#機能の紹介
以下に取り上げる機能は、すべてユーティリティクラス STUtil の static メソッドとして提供されます。javadoc は [こちら](http://jutaime.hotchpotch.xyz/docs/api/index.html?xyz/hotchpotch/jutaime/serializable/STUtil.html)、ソースコードは [こちら](https://github.com/nmby/jUtaime/blob/master/project/src/main/java/xyz/hotchpotch/jutaime/serializable/STUtil.java) を参照してください。

##オブジェクトのシリアライズ／デシリアライズ検証を支援する機能
例えば Serializable な MyClass クラスを実装したとして、実際にシリアライズ／デシリアライズできるかを確認したい。
そんなときはこれだけで OK です。

```java:
assertThat(STUtil.writeAndRead(new MyClass()), instanceOf(MyClass.class));
```

`writeAndRead(Object)` メソッドは、引数として受け取ったオブジェクトを内部でバイト配列にシリアライズしたのちデシリアライズしてオブジェクトに復元し、返します。

シリアライズ／デシリアライズの過程で IOException 等の例外が発生した場合は、以下の実行時例外にラップしてスローします。

* FailToSerializeException
* FailToDeserializeException

いずれも RuntimeException のサブクラスですので、呼び出し元で try-catch や throws 句を実装する必要はありません。

シングルトンなどのインスタンス制御の検証も、このメソッドを利用して行えます。戻り値は元のオブジェクトの型 T ですので、メンバの検証も簡単です。

```java:
assertThat(STUtil.writeAndRead(OnlyOne.getInstance()), sameInstance(OnlyOne.getInstance()));
assertThat(STUtil.writeAndRead(new Odd(7)).get(), is(7));
```

##シリアライズ形式の理解を支援する機能
オブジェクトがどのような形式にシリアライズされるのかを理解することは、しばしばテストの役に立ちます。

```java:サンプルコード1
System.out.println(STUtil.toHexString(STUtil.write(new Odd(7))));
```

```text:出力結果1
ac ed 00 05 73 72 00 09 6d 79 70 6b 67 2e 4f 64 64 00 00 00 00 00 00 00 01 02 00 01 49 00 01 6e 78 70 00 00 00 07
```

`write(Object)` メソッドは、オブジェクトをシリアライズして得られるバイト配列を返します。`toHexString(byte[])` メソッドは、バイト配列を16進表示形式の文字列に変換します。

次の `bytes` メソッドは、プリミティブデータ型や文字列型などをシリアライズして得られるバイト配列を返します。この出力結果2を先ほどの出力結果1と比較することにより、どの部分がクラス名を表しどの部分がフィールド値を表すのかの確認が容易になります[^1]。

```java:サンプルコード2
System.out.println(STUtil.toHexString(STUtil.bytes(Odd.class.getName())));
System.out.println(STUtil.toHexString(STUtil.bytes(7)));
```

```text:出力結果2
00 09 6d 79 70 6b 67 2e 4f 64 64
00 00 00 07
```

##バイト配列の編集と、それを用いたデシリアライズ検証を支援する機能
`replace(byte[], byte[], byte[])` メソッドはバイト配列の部分配列1に一致する部分を部分配列2で置換します。`read(byte[])` メソッドは、バイト配列をデシリアライズして得られるオブジェクトを返します。
いずれも、バイト配列を改竄してテストを行う際に便利です。

```java:
byte[] original = STUtil.write(new Odd(7));
byte[] modified = STUtil.replace(
        original,
        STUtil.bytes(7),
        STUtil.bytes(3));

Odd odd = (Odd) STUtil.read(modified);
assertThat(odd.get(), is(3));
```

`writeModifyAndRead(Object, Function<byte[], byte[]>)` メソッドは、オブジェクトのシリアライズ、バイト配列の改竄、デシリアライズの一連の操作を行うのに適しています。

```java:
assertThat(STUtil.writeModifyAndRead(
        Integer.valueOf(1),
        original -> { original[80] = 0x02; return original; }),
        is(Integer.valueOf(2)));
```

以上で取り上げた以外にも、いくつかのメソッドが [提供されています](http://jutaime.hotchpotch.xyz/docs/api/index.html?xyz/hotchpotch/jutaime/serializable/STUtil.html)。
これらを利用することにより、[『Serializable実装クラスのテストについて考える』シリーズ](http://qiita.com/nmby/items/6ffc244537087438dcfd) で取り上げたようなシリアライズ／デシリアライズに関する検証を JUnit で効率的に行うことができます。

#使い方
[こちら（GitHub）](https://github.com/nmby/jUtaime/releases) から xyz.hotchpotch.jutaime-X.X.X-yyyymmdd.jar をダウンロードしてビルドパスに追加することで利用できます。
本稿の中では敢えてしませんでしたが、実際に使用する際には `STUtil.*` を static インポートしておくと便利でしょう。

実際にこのライブラリを利用して、以前の投稿[『リバーシで遊んで覚える Java 8.』](http://qiita.com/nmby/items/bd44e28d937108fa3eb0)で紹介したソースコードに対するシリアライズ／デシリアライズに関するテストを [行っています](https://github.com/nmby/reversi/tree/master/project/src/test/java/xyz/hotchpotch/reversi/core)。（ただし道半ばですが。。。）

　～　～　～

本稿の内容や機能について何かお気づきの点がありましたら、コメントなどいただけますと有難いです。

#注釈
[^1]: オブジェクトのシリアライズ形式の詳細については、[Oracle: Java開発者ガイド](http://docs.oracle.com/javase/jp/8/docs/index.html) の中の [こちらのページ](http://docs.oracle.com/javase/jp/8/docs/platform/serialization/spec/protocol.html) で確認することができます。バイト配列の目視比較による確認は楽ですが、プログラマとしては仕様（の概要）を押さえておくべきと考えます。