テストで使っているJMockitというモックライブラリが古いバージョンだったので、最新バージョンにアップデートする作業をしたのですが、その際にちょっとしたハックをしたので紹介します。
なお、JMockit自体の変更点などについては別記事にしているので、JMockitを使っている方はそちらもご覧ください(→JMockit と共に生きる者ためのメモ - Qiita)
どんな問題があったのか?
JMockit(の旧バージョン)では以下のように Expectations
のインスタンスイニシャライザでメソッドを差し替えます。戻り値は result
または returns
で指定します。
new Expectations() {{
intList.get(0); result = 1; // intList.get(0) が常に1を返すようになる
intList.get(0); returns(1); // ↑ の別記法(1.30で廃止)
intList.get(1); returns(-1, 1); // intList.get(1) が、-1 と 1 を交互に返すようになる
}};
ここで、1引数版のreturns(v)
は JMockit 1.30 で廃止されてしまいました。
そのため、テストコード中の returns(v)
は result = v
に書き換えなくてはなりません。
まず、sed で置換しようとしましたが、returnsには1引数版と2引数以上版があるので単純に置換することはできません。
sed -e 's/returns/result =' // 2引数版も置換してしまう。-e 以外の引数は省略
正規表現を工夫したりもしましたが「returnsの引数として別のメソッド呼び出しを指定する」コードがある等して、上手くいきませんでした。
次に IntelliJ のリファクタリング機能を探しましたが、
- インライン化
- メソッド名変更
などはあるのですが「メソッド呼び出しを代入文に変更」という変更はできませんでした1。
どうやって解決したか?
まず、returns
の呼び出しを自作クラスのメソッドの呼び出しに置換しました。
sed -e 's/returns(/test.MyDummy.returns(/g'
テストコードはこんな形になります。
new Expectations() {{
intList.get(0); test.MyDummy.returns(1); // returns が自作メソッドのものに置換されている
intList.get(1); test.MyDummy.returns(-1, 1); // 2引数版も置換される
}};
さらに、自作クラスに以下のようにメソッドを定義します。この時点で、JavaコードとしてはValidになるので、IntellJのリファクタリング機能を使えるようになります。
class MyDummy {
public static void returns(Object o) {
result = o;
}
public static void returns(Object o, Object o2, Object... args) {
}
}
IntelliJ で、MyDummy.returnsにカーソルを合わせ Refactor This → inline
でインライン化します。
すると、IntelliJ は、メソッドオーバーロードを理解しているので、1引数版の returns のみをインライン化してくれます。
new Expectations() {{
intList.get(0); result = 1;
intList.get(1); test.MyDummy.returns(-1, 1);
}};
これで「1引数のreturns
→ result
」は完了です。残った test.MyDummy.returns
は、再びsed で置換して、returns
に戻します。
sed -e 's/test.MyDummy.returns(/returns(g'
これで、1引数のreturns
のみが書き換えられた状態になりました。
new Expectations() {{
intList.get(0); result = 1;
intList.get(1); returns(-1, 1);
}};
かかった時間は、考える時間も含めて1・2時間ほどでした(測ってないけど多分)。returns
は何千箇所もあったので、手作業では何日もかかったことでしょう。
また、使った変換は全て機械的なものだったので、バグは生じませんでした(本当にバグっていないかは、実際にテストをコンパイル・実行して確認できました)。
-
商用版では出来たりするかもしれませんが、私が使っていたIntelliJ IDEAでは出来ませんでした。 ↩