QuickCheck でテストデータを半自動生成する

# 概要
Property Based Test を実装するためのライブラリ「[QuickCheck](https://ja.wikipedia.org/wiki/QuickCheck)」の Java 移植版を試してみました。

## Property Based Test とは
以前の渋谷Javaで [@gakuzzzz](https://twitter.com/gakuzzzz) さんがおっしゃっていたのによると下記のようなものだそうです。

* テストデータを半自動生成……テストデータを管理する必要がない
* すべての値について性質を満たすことをテスト
* 機能変化や仕様変化に強い
* Java にも QuickCheck ファミリーのライブラリがある

テストデータを作るのが面倒な場合や、あらゆる値を網羅的にテストしたいケースで役立つようです。なお、境界値テストのような固定の値を必要とするものは、通常通りの JUnit Test を書けばよいとのことでした。

## Java の QuickCheck 実装
[QuickCheck は Haskell のライブラリ](https://hackage.haskell.org/package/QuickCheck)です。今回は Java 移植版の [net.java.quickcheck](https://bitbucket.org/blob79/quickcheck) を使ってみました。最終コミットが2012年で、Bitbucket 上のドキュメントも2015年以降更新されていないので、開発が止まっているようです。

# 導入

## 依存の追加
build.gradleに下記の依存を追加します。

```groovy:dependencies
dependencies {
    testCompile 'net.java.quickcheck:quickcheck:0.6'
}
```

[Maven 等の場合はこちら](http://search.maven.org/#artifactdetails|net.java|quickcheck|0.6|jar)

# とりあえず動かす
CombinedGenerators で Collection の型を指定して、PrimitiveGenerators で要素の型を指定します。
私の見たサンプルだとどちらも static import して使っていました。~~サンプルとしてはどうなんだろうと思わなくもないです。~~

```java:とりあえず動かしてみる
    @Test
    public void simple() {
        final Generator<List<Integer>> generator = lists(integers());
        IntStream.range(0, 4).forEach(i -> System.out.println(generator.next()));
    }
```

実行結果はこちら

```:実行結果
[-1473595787, -1881741363, 1002236653, 864642487]
[-1453342830, 1645866063, -1615158593]
[936501104, 1232292532, 1130963563]
[]
```

たまに空っぽの List を返してくるのは何なのか？と思ったら、 lists では空の List も生成されるようで、必ず中身のある List が欲しい場合には CombinedGenerators#nonEmptyLists を使えばよいらしいです。

## ひな形
今回は下記のひな形を使っていろいろ試します。
generator の next() メソッドを4回呼び出して、生成されたオブジェクトを表示するだけの簡単なコードです。

```java:Template
@Test
public void simple() {
    final Generator<？> generator = /* ここに Generator を書く */;
    IntStream.range(0, 4)
             .mapToObj(i -> generator.next())
             .forEach(System.out::println);
}
```

## 実行環境
Java SE8 を使用します。

```
$ java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)
```

---

# PrimitiveGenerators
単一の値を生成する Generator の Factory です。PrimitiveGenerators にはいろいろな型の Generator を作成するファクトリメソッドが用意されています。

## integers

### 最小値を指定
```java:integers
PrimitiveGenerators.integers(7) // 最小値を指定
```

```:実行結果
[803228871]
[2126942855]
[1950900576, 1146603069]
[1122380821, 1295774766, 1614686771, 1940120700, 1356357892, 1258900587, 1020490335]
```

そもそもの最大値が大きすぎるので、あまり役に立つ場面が思い浮かびません。

#### 桁数を揃える
Integer の最大桁数で統一させてみます。

```java:integers
PrimitiveGenerators.integers(1000000000)
```

```:実行結果
1878704899
1246214129
1802857637
1564484122
```

### 最小値と最大値を指定
通常はこれを使うことになるでしょう。

```java:integers
PrimitiveGenerators.integers(7, 10) // 最小値と最大値を指定
```

```:実行結果
10
10
9
7
```

### 正の整数のみ生成
PrimitiveGenerators.positiveIntegers() を使うと、正の整数だけを生成できます。

```:実行結果
2011848657
558263171
1621972212
291054580
```

ほかに Long や Double や Byte でも同様のことが可能です。

# strings
下記は最大7文字までの文字列を生成させます。

```java
PrimitiveGenerators.strings(7)
```

実行結果はこちら

```:実行結果
)[d^t
5gL&e
k]tI|T!
CtHE1
```

およそ実用的とは言えない文字列が出来上がりました。

### 使用可能な文字列を制限

```java:使用可能な文字列を制限
PrimitiveGenerators.strings("abcdefghijk")
```

実行結果はこちら

```:実行結果
kc
iaeafcegaddeficjjgddeajbkcj
bijabhadejk
dcecggjjigajikjajaciiaaeh
```

### A-Zの文字列のみを使う
PrimitiveGenerators.letterStrings() を使うことにより、 A-Zの文字列のみで文字列を生成できます。

```:実行結果
XJUKLGorEXahQKsseOcdJ
ftgGaxfweNpyrDtrRkPOnqowfec
nVSghoRXzyBcEYjpNCo
MTDvouHmpayKMnSt
```


# いろいろなGenerator

## PrimitiveGenerators.nulls()
null だけを生成します。何に使うのかまったくわかりません。

```:実行結果
null
null
null
null
```

## PrimitiveGenerators.dates()
Date クラスのオブジェクトを生成します。結構使いどころあるのじゃないかと思ったところ、

```
Tue Jan 25 11:16:33 JST 225078784
Mon Jun 12 03:52:58 JST 286379026
Wed May 21 16:47:15 JST 199363913
Mon Jul 11 08:05:45 JST 1400664
```

とんでもない未来が生成されました。

実際には dates(long low, long high) を使うことになるでしょう。

```java
final Generator<Date> generator = PrimitiveGenerators.dates(0L, System.currentTimeMillis());
```

```:実行結果
Wed Mar 21 21:22:28 JST 1984
Thu Jul 11 18:35:57 JST 2013
Sat Jun 15 11:26:13 JST 2013
Wed Sep 03 07:23:46 JST 1980
Sat Jun 04 17:51:33 JST 2016
```

## PrimitiveGenerators.fixedValues(value);
渡されたオブジェクトと同じ値のものを返します。可変長引数のものも Overload されているので複数渡せます。

### 1つの場合

```java:1つの場合
PrimitiveGenerators.fixedValues(new Point(112, 120));
```

何度 next() を呼んでも同じ値が返ってきます。

```:実行結果
java.awt.Point[x=112,y=120]
java.awt.Point[x=112,y=120]
java.awt.Point[x=112,y=120]
java.awt.Point[x=112,y=120]
```

### 複数の場合

```java:複数の場合
PrimitiveGenerators.fixedValues(new Point(-44, 10), new Point(112, 120), new Point(132, 190));
```

毎回異なる値が返ってきます。同じ値が続けて2回返ってくることもあります。

```:実行結果
java.awt.Point[x=132,y=190]
java.awt.Point[x=-44,y=10]
java.awt.Point[x=132,y=190]
java.awt.Point[x=112,y=120]
```


# CombinedGenerators
CombinedGenerators では複数要素を束ねるオブジェクト(要するに配列や Collection)を生成します。

ちなみに、生成できうる値の範囲がごく小さいと、Set の生成で例外が発生します。

```java
@Test
public void simple() {
    final Generator<Set<Integer>> generator
            = CombinedGenerators.nonEmptySets(PrimitiveGenerators.integers(7, 10));
    IntStream.range(0, 4)
             .mapToObj(i -> generator.next())
             .filter(l -> !l.isEmpty())
             .forEach(i -> System.out.println(generator.next()));
}
```

```:Stacktrace
net.java.quickcheck.GeneratorException: Failed to generate another value after [100] tries (generator: net.java.quickcheck.generator.support.IntegerGenerator@9e89d68)
	at net.java.quickcheck.generator.support.VetoableGenerator.next(VetoableGenerator.java:56)
	at net.java.quickcheck.generator.support.ListGenerator.next(ListGenerator.java:61)
	at net.java.quickcheck.generator.support.SetGenerator.next(SetGenerator.java:48)
	at net.java.quickcheck.generator.support.SetGenerator.next(SetGenerator.java:25)
	at jp.toastkid.verification.rxjava.QuickCheckVerification.lambda$0(QuickCheckVerification.java:20)

……省略……
```

# Generator を作ってみる
例えば、このような Pojo があるとします。

```java:Pojo
private class Pojo {
    private String name;
    private int    price;

    public Pojo(final String name, final int price) {
        this.name  = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return this.name + " " + this.price;
    }
}
```

Generator は次のような Interface です。

```java:Generator.java
public interface Generator<T> {

	/**
	 * Generates the next instance.
	 * 
	 * @return a newly created instance
	 */
	public T next();
}
```

お気づきの通り、Lambda 式で定義可能です。各値はそれぞれ PrimitiveGenerators で Generator を生成します。

```java:OriginalGenerator
final Generator<Pojo> generator = () -> {
    final Pojo p = new Pojo();
    p.name  = PrimitiveGenerators.strings("abcdefg").next();
    p.price = PrimitiveGenerators.integers(1, 10).next();
    return p;
};
```

```:実行結果
acaaacfegggfffeggec 9
ddfbdffffafbfdgddfcc 10
gbafcedb 2
bfg 7
```


## サンプルコード
1.[Test codes](https://bitbucket.org/blob79/quickcheck/src/c8e12cc026fd6295966bf9ff5d20919961ff4149/quickcheck-examples/src/test/java/?at=default)

## Related works
1.[junit-quickcheck](https://github.com/pholser/junit-quickcheck/tree/6447506d4840cd11bf647e4e7e175a4df6037ad0)……こちらは今年に入ってもコミットがあるので、今も継続的に開発されているようです。~~こっちを調べればよかった~~