【JMockit1.30】 Expectationsでの引数マッチングをさらに詳しく

JMockitのExpectationsの引数マッチングについて、詳しく調べると結構いろんなことができるよっていう紹介です。

# 前提条件

基本的な機能には触れません。
以下の記事がすごく分かりやすいです。
[JMockit使い方メモ](http://qiita.com/opengl-8080/items/a49d4dae9067413ccdd6)
上記に載ってないこと・変更されたことがメインです。

# 環境
JMockit1.30

# NonStrictExpectationsについて
ver1.25 で削除されています。
`Expectations`でminTimes = 0を指定することでNonStrictExpectationsの代わりを行うことが出来ます。

# 引数のマッチング

```java:Logic.java
/**
 * テスト対象のクラス.
 */
public class Logic {

    // DI
    private Hoge hoge;

    // テスト対象のメソッド
    public void main() {

        UserModel user = new UserModel("A123", "田中", 30);

        String str = hoge.method(user);
        
        ...
    }
}
```
```java:Hoge.java
/**
 * DIされるクラス.
 */
public class Hoge {

    public String method(UserModel user) {
        return "abc";
    }

}
```
```java:UserModel.java
/**
 * User情報.
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserModel {

    /**
     * ユーザID.
     */
    private String id;

    /**
     * 名前.
     */
    private String name;

    /**
     * 年齢.
     */
    private int age;

}
```

上記で` Logic.main() `を実行した際の` hoge.method(UserModel) `の引数について検証を行う。

## 基本のマッチング

```java:LogicTest.java
public class LogicTest {

    @Tested
    private Logic target;

    @Injectable
    private Hoge hoge;

    @Test
    public void 基本のマッチング() {

        // Setup
        UserModel assume = new UserModel() {
            {
                setId("A123");
                setName("田中");
                setAge(30);
            }
        };

        new Expectations() {
            {
                // Verify
                hoge.method(assume);
                times = 1;
            }
        };

        // Exercise
        target.main();

    }
```

- 実行メソッドの引数に想定するオブジェクトのインスタンスを置くことで、実行時のインスタンスと想定するオブジェクトのインスタンスを` Object.equals `で比較を行うことができる。
- lombokの` @Data `を利用した場合、` Object.equals `は各プロパティ値の比較となる。
- 上記の例は` hoge.method(UserModel) `の実行時の引数のユーザ情報がid   = A123, name = 田中, 年齢 = 30であることを確認している。


## Matcherでのマッチング

```java:LogicTest.java
public class LogicTest {

    @Tested
    private Logic target;

    @Injectable
    private Hoge hoge;

    @Test
    public void Matcherでのマッチング() {

        new Expectations() {
            {
                // Verify
                hoge.method(withArgThat(
                    allOf(hasProperty("id", is("A123")), hasProperty("name", is("田中")))));
                times = 1;
            }
        };

        // Exercise
        target.main();

    }
```

- ` withArgThat(Matcher) `を用いることで、Hamcrest の Matcher を使って引数をマッチングすることができる。
- 上記の例は` hoge.method(UserModel) `の実行時の引数のユーザ情報がid   = A123, name = 田中であることを確認している。

## Delegateでのマッチング

```java:LogicTest.java
public class LogicTest {

    @Tested
    private Logic target;

    @Injectable
    private Hoge hoge;

    @Test
    public void Delegateでのマッチング() {

        new Expectations() {
            {
                // Verify
                hoge.method(with(new Delegate<UserModel>() {
                    boolean delegage(UserModel user) {
                        return user.getAge() >= 20;
                    }
                }));
                times = 1;
            }
        };

        // Exercise
        target.main();

    }
```

- ` with(Delegate) `を用いることで、メソッドでアサーションを行うことが出来る。
- Delegateの使い方はメソッド動作指定の場合とほとんど同じ。[JMockit使い方メモ](http://qiita.com/opengl-8080/items/a49d4dae9067413ccdd6)の[Delegate を使ってモック化したメソッドの動作を指定する]を参照。
- 戻り値の型をbooleanにしてアサーションを行う。
- 上記の例は` hoge.method(UserModel) `の実行時の引数のユーザ情報の年齢が20以上であることを確認している。

## withCaptureでのマッチング
別の例で説明します。` Hoge `と` UserModel `については前回と同じ。

```java:Logic.java
/**
 * テスト対象のクラス.
 */
public class Logic {

    @Inject
    private Hoge hoge;

    public void main2() {

        List<UserModel> users = Arrays.asList(
            new UserModel("A123", "田中", 30),
            new UserModel("A124", "佐藤", 18), 
            new UserModel("A125", "鈴木", 25));
        
        users.stream()
                .map(hoge::method)
                .forEach(System.out::println);
    }

}
```

```java:LogicTest.java
public class LogicTest {

    @Tested
    private Logic target;

    @Injectable
    private Hoge hoge;

    @Test
    public void Captureでのマッチング() {

        // SetUp
        List<UserModel> captures = new ArrayList<>();

        new Expectations() {
            {
                hoge.method(withCapture(captures));
                times = 3;
            }
        };

        // Exercise
        target.main2();

        // Verify
        assertThat(captures, hasSize(3));
        assertThat(captures,
            hasItem(allOf(hasProperty("id", is("A125")), hasProperty("name", is("鈴木")))));

    }
```

- ` withCapture(List) `を用いることで、引数を指定したリストに取得することができる。
- Listの実装は` ArrayList `などmutableなリストを使うこと。
- 上記の例は` hoge.method(UserModel) `が三回実行され、その中でid = A125かつname = 鈴木のユーザが存在することを確認している。

# 参考

[JMockit使い方メモ](http://qiita.com/opengl-8080/items/a49d4dae9067413ccdd6)