JMockit1.4→1.30へのアップデート時にコケたことまとめ 

Java8u74→8u102ぐらいに上げた際に、どうやらJMockitも上げなきゃならんってことになりましたが、
使ってるJMockitが古すぎて、コンパイルエラーになるやら、実行時エラーになるやらわりとてんやわんやだったので、
備忘録としてまとめることにしました。
※随時更新中

## 環境
Java8u102(8u74でもたぶん動くかと)
maven3系

## change log
だいたい書いてあるけど、実際にどう書けばいいのかわからないのもありつつ
[JMockit Development history](http://jmockit.org/changes.html)

## 主に参考にさせていただいたサイト
- [JMockit使い方メモ](http://qiita.com/opengl-8080/items/a49d4dae9067413ccdd6)
だいたい載ってて、しかも、詳しく書き方も載っててマジ感謝です。

## まずはライブラリのバージョンをアップ
### JMockit 1.4

```pom.xml
		<dependency>
			<groupId>com.googlecode.jmockit</groupId>
			<artifactId>jmockit</artifactId>
			<version>1.4</version>
			<scope>test</scope>
		</dependency>
```

### JMockit 1.30
groupすら変わってることにビビります。(よくあるけどさ・・・)
※追記：ご指摘いただきました。昔から2つグループがあったらしいです。が、統合されたんですかね？

```pom.xml
		<dependency>
			<groupId>org.jmockit</groupId>
			<artifactId>jmockit</artifactId>
			<version>1.30</version>
			<scope>test</scope>
		</dependency>
```

## 1. コンパイルエラーを解消する
###  NonStrictExpectationsをExpectationsに変更する
Version 1.23でDeprecated、Version 1.25で削除になってます。
> Version 1.23 (Apr 24, 2016):
Deprecated the NonStrictExpectations class. Existing tests should use Expectations instead.

> Version 1.25 (Jun 26, 2016):
Removed the NonStrictExpectations class, which was deprecated in version 1.23.

とりあえず、コンパイルエラーをなんとかしたいので、盲目的に変えていきます。

NonStrictExpectationsの使い方は以下のリンクでまとめてくださってます(もう使えないけど。。)
[jMockitがいいよ Verification編](http://qiita.com/NewGyu/items/03f5ec400abf010d2b7b)
#### NonStrictExpectationsとExpectationsとの違いは

- Expectations 実行順序、実行回数などすべて記録。かなり厳密。
- NonStrictExpectations ちょっとゆるめ。実行されてもされなくてもスルーされる。

みたいな違いがあるらしいです。詳しくは以下のリンクでわかりやすくまとめてくださってます。感謝。
[JMockit使い方メモ NonStrictExpectations](http://qiita.com/opengl-8080/items/a49d4dae9067413ccdd6#nonstrictexpectations)
テスト実行時に落ちるので、これは後ほど修正します。

### @Mocked(methods="readLine")とかアノテーション内の引数を指定してるやつ
Version 1.5でDeprecated 、Version 1.6で『methods』『inverse』属性は削除されてます。
> Version 1.5 (Oct 20, 2013):
Deprecated the "methods" and "inverse" attributes of @Mocked. Existing uses like @Mocked(methods = {"method1", "method2"}) should simply be rewritten as @Mocked({"method1", "method2"}) (using the default value attribute). As for the inverse attribute, specify what is to be mocked instead.
> Version 1.6 (Dec 23, 2013):
Removed the "methods" and "inverse" attributes of the @Mocked annotation, which were deprecated in release 1.5. Tests still using "@Mocked(methods = {...}) should instead use the default "value" attribute.

今度から@Mocked(methods = {...})使ってね、と言ってるわりにはこの方法もVersion 1.19で削除されちゃいます。悲しい。
> Version 1.19 (Aug 23, 2015):
Removed the "value" attribute of @Mocked, which was deprecated in release 1.17. Existing tests using "@Mocked({"someMethod", "anotherMethod", ...})" should simply omit the attribute, or use partial mocking as applied with new Expectations(objToPartiallyMock) { ... }, or apply a MockUp<T> class.

Expectationsの引数で書く方法は、その後もいろいろゴニョゴニョあって、使えるんだか使えないんだかわからないので、
おとなしくMockUp<T>に書き換えます。

```修正前.java
@Test
public void test(@Mocked(methods="readLine") final BufferedReader br) {
		new NonStrictExpectations() {
			{
				br.readLine();
				result = new IOException();
			}
		};
}
```

``` 修正後.java
@Test
public void test() { // MockUp<T>で定義するので、引数は削除する。あるとエラーになる
		new MockUp<BufferedReader>() {
			@Mock
			public String readLine() throws IOException{
				throw new IOException();
			}
		};	
}
```

ちなみに、BufferedReaderのこの場合だと、@Mockedを@Injectableに変えても良さそう(というか、どちらも同じ結果になった)
どっちがいいのかなぁ。
詳しくはこちら→[JMockit はじめの一歩 @Injectable](http://qiita.com/hitomatagi/items/95ba1bc6a630bac16f9e#injectable)

### setField, invokeはDeencapsulationのstaticメソッドに変更する
setField, invokeとかprivateなやつらに対して操作を行うのは、Invocationsクラスだったようなんですが、Version 1.6にて削除されたようです。
> Version 1.6 (Dec 23, 2013):
Removed all Reflection-based utility methods from the Invocations class (the base for Expectations and Verifications), in favor of using the static methods of identical signatures in the Deencapsulation class. This change in the API will not require any changes to test code, except that test classes using these methods should now include the following "static import": import static mockit.Deencapsulation.*;

```修正前.java
setField(hoge, "aaaa", "aaaa");
invoke((hoge, "privateMethod");
```

```変更後.java
Deencapsulation.setField(hoge, "aaaa", "aaaa");
Deencapsulation.invoke((hoge, "privateMethod");
```

ちなみに、private static finalのフィールドにセットしようとすると、Can not set static finalって怒られる場合があるので、その場合は後述[Deencapsulation.setFieldの挙動が変わったらしい](http://qiita.com/yukiko-kato-bass/items/69b0c7b2417079345d37#deencapsulationsetfield%E3%81%AE%E6%8C%99%E5%8B%95%E3%81%8C%E5%A4%89%E3%82%8F%E3%81%A3%E3%81%9F%E3%82%89%E3%81%97%E3%81%84)を参考に。

## 2. テストでエラーになるものを修正する
### Missing 1 invocation to: ～ Caused by: Missing invocations
これでコケるやつは[NonStrictExpectationsをExpectationsに変更する](http://qiita.com/yukiko-kato-bass/items/69b0c7b2417079345d37#nonstrictexpectations%E3%82%92expectations%E3%81%AB%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B)の影響です。
このメソッド呼ばれてねーよ！って怒っております。サーセン。
メソッドの記録が厳密になった故のエラーですね。
回避策としては、メソッドの最小期待実行回数を0にしちゃいます。
ちなみに、呼ばれていないことを明示的に示したい場合は、maxTimes=0でチェックですれば良さそう。

```HogeTest.java
        new Expectations() {{
            hoge.method(); minTimes=0;
            hoge.method2(); maxTimes=0;
        }};

```

~~まあ、これをつけると、呼ばれたか呼ばれてないかわからなくなるので、その辺はVerificationsでチェックするようにしたらいいんじゃないかなと。~~
※追記：maxTimes=0;でチェックできるじゃんと気づきました。


### EnumのMockの書き方が変わった？
こんな書き方してるやつが、NullPointerExceptionで落ちるようになった。

```修正前.java
@Test
public void test(@Mocked final HogeEnum hogeEnum) {

	new Expectations() {

		@Mocked
		HogeEnum hogeEnum;

		{
			hogeEnum.getHoge();
			result = "hogehoge";
		}
	};

```

どうやらMockUpに書き換えれば良いらしい。
[How to mock this in an Enum class using jmockt?](http://stackoverflow.com/questions/23188902/how-to-mock-this-in-an-enum-class-using-jmockt)

```修正後.java
@Test
public void test() { // MockUpで書くので、引数は消す
	new MockUp<HogeEnum>() {
		@Mock
		public String getHoge() {
			return "/NOTHING/DATA.mf";
		}
	};
}
```

### NonStrictExpectations→Expectationsに変えたことによるMockインスタンスの挙動変更？
こんな書き方してたやつをNonStrictExpectations→Expectationsに変えたら、failになってしまった。

``` 変更前.java
    @Test
    public void test() throws Exception {
    	final Hoge hoge1 = new Hoge("hogehoge");
		new NonStrictExpectations() {
            {
                test.getHoge("hogehoge");
                result = hoge1;
            }
        };
        
        assertThat(test.getHoge("hogehoge"), hoge1);
        
        assertThat(test.getHoge("fugafuga"), null);  // ここでnullにはならない！
    }
```

デバックしてみたら、Hogeのインスタンス自体は作られてしまってた。(valueとかはnullなんだけど)
ここ見ると、[JMockit使い方メモ モック化されたオブジェクトのメソッドが返す値](http://qiita.com/opengl-8080/items/a49d4dae9067413ccdd6#%E3%83%A2%E3%83%83%E3%82%AF%E5%8C%96%E3%81%95%E3%82%8C%E3%81%9F%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%8C%E8%BF%94%E3%81%99%E5%80%A4) とあるので、挙動通りではあるのですが、引数によって返すオブジェクト変えたいっていうか、ある引数以外のときはnull返したいんですよ。
※追記：nullを返したい場合は明示的に指定しないとだめらしい。ここに書いてました。→ [JMockit使い方メモ モックが返す値を null にしたい場合](http://qiita.com/opengl-8080/items/a49d4dae9067413ccdd6#%E3%83%A2%E3%83%83%E3%82%AF%E3%81%8C%E8%BF%94%E3%81%99%E5%80%A4%E3%82%92-null-%E3%81%AB%E3%81%97%E3%81%9F%E3%81%84%E5%A0%B4%E5%90%88)
※2017/01/20追記：[7tsunoさん](http://qiita.com/7tsuno)よりコメントいただきました。きっちりやるならDelegate使うのが良いですね。詳しくは→[Delegateでのマッチング](http://qiita.com/7tsuno/items/2dcebe2fc1533fb68a4d#delegate%E3%81%A7%E3%81%AE%E3%83%9E%E3%83%83%E3%83%81%E3%83%B3%E3%82%B0)

#### 修正方法
引数違う場合のresultを明示的に指定する

``` 修正案.java
    @Test
    public void test() throws Exception {
    	final Hoge hoge1 = new Hoge("hogehoge");
		new Expectations() {
            {
                test.getHoge("hogehoge");
                result = hoge1;
                test.getHoge("fugafuga");
                result = null;
            }
        };
        
        assertThat(test.getHoge("hogehoge"), hoge1);
        
        assertThat(test.getHoge("fugafuga"), null);
    }
```

### Deencapsulation.setFieldの挙動が変わったらしい
private static final なフィールドの書き換えが1.4では動いてたんですが、どうやら挙動が変わったらしく、java.lang.RuntimeException: java.lang.IllegalAccessException: Can not set static final ～　が発生するようになりました。(セットする対象はプリミティブではないです。セットしたのはMockオブジェクト。)
[Deencapsulation.setField() is broken for JDK8 after I upgrade to v1.24](https://github.com/jmockit/jmockit1/issues/281)
> Ok, I updated our tests to use a MockUp instead. This is cleaner anyway.

え、MockUp 使えってことですか？
というわけで、[static 初期化ブロックをモック化する](http://qiita.com/opengl-8080/items/a49d4dae9067413ccdd6#static-%E5%88%9D%E6%9C%9F%E5%8C%96%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E3%82%92%E3%83%A2%E3%83%83%E3%82%AF%E5%8C%96%E3%81%99%E3%82%8B)を参考に使って書き直してみました。

```Hoge.java
// Applicationは自作クラスです
private final static Application application = Application.getInstance();
```

``` 修正前.java
Hoge hoge = new Hoge();
Deencapsulation.setField(Hoge.class , application);
```

``` 修正後.java
		new MockUp<Hoge>() {
			@Mock
			void $clinit() {
				Deencapsulation.setField(Hoge.class ,hoge);
			}
		};
```

staticブロックで宣言してなくても$clinit()で代入出来るのか。謎。

### Deencapsulation.invokeも挙動が変わったみたい
invokeも一部駄目でした。動くやつと動かない差がわからない。。
※Expectations内でinvokeしてるやつは、MockUpで書かないと駄目っぽいです。ただいま確認中
これもMockUp使うと動きました。MockUp強すぎ。

```PrivateHoge.java
// こんなメソッドがあったとする
private Hoge getHoge() {
  return hoge;
}
```

```修正前.java
new Expectations() {{
  Deencapsulation.invoke(privateHoge, "getHoge");
}};
```
MockUp使って書き直し

```修正後.java
		new Expectations() {{
			new MockUp<PrivateHoge>() {
				@Mock
				Hoge getHoge() {
					return hogehoge;
				}
			};
		}};
```

MockUp内のMock化したメソッドなら、Deencapsulation.invokeも使えるみたいです。
NullPointerって言ってるから、明示的にしてからinvokeしろってことなのかなぁ

```修正案その2.java
		new Expectations() {{
			new MockUp<PrivateHoge>() {
				@Mock
				Hoge getHoge() {
					Deencapsulation.invoke(privateHoge, "getHoge");
				}
			};
		}};
```

### 動的部分モック化の書き方が変わった模様
Expectations内で@Mockedインスタンス宣言してたやつらがnullを返すようになりました。
ちなみに部分モックのことをPartial Mockingと言うらしいです。へぇー
[jMockit Verifications 部分モッキングとか](http://qiita.com/NewGyu/items/57b4efe0c99f779ac066)

```動かなくなったやつ.java
		new Expectations(patternManager) {
			@Mocked Hoge hoge;
			{
				System.out.println(hoge);
				System.out.println(hoge.hoge);

```

#### 修正案
1. 単体テストのインスタンスフィールドに変える
2. 単体テストの実行メソッドの引数で@Mocked使って宣言する→
[テストメソッドの引数で定義する](http://qiita.com/opengl-8080/items/a49d4dae9067413ccdd6#%E3%83%86%E3%82%B9%E3%83%88%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%AE%E5%BC%95%E6%95%B0%E3%81%A7%E5%AE%9A%E7%BE%A9%E3%81%99%E3%82%8B)

のどちらかで。

### コンストラクタの戻り値は返せなくなった
[コンストラクタの戻り値は指定できない](http://qiita.com/opengl-8080/items/a49d4dae9067413ccdd6#%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF%E3%81%AE%E6%88%BB%E3%82%8A%E5%80%A4%E3%81%AF%E6%8C%87%E5%AE%9A%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84) 

これが結構難儀で格闘中

#### 代替案①
[Accessing the invocation context](http://jmockit.org/tutorial/Faking.html#invocation) にあるように$initメソッドでコンストラクタと同じ処理が出来るっぽい。シグニチャ合わせればよい。

```HogeTest.java
new MockUp<File>() {
      @Mock
      void $init(File file, String path)
      {
        // new File(file, path) のコンストラクタをモック化出来る
      }
```
ちなみに、Invocationを先頭の引数に加えると、モックインスタンスも取れるっぽい。

### SystemクラスとかネイティブクラスもMockUpで書けばいいらしい


## その他、参考にさせていただいたサイト様
- [JMockit はじめの一歩](http://qiita.com/hitomatagi/items/95ba1bc6a630bac16f9e)
- [【JMockit1.30】 Expectationsでの引数マッチングをさらに詳しく](http://qiita.com/7tsuno/items/2dcebe2fc1533fb68a4d)