LoginSignup
1
0

More than 3 years have passed since last update.

私は如何にして JMockit の returns(Object) を安全に置換したか?

Last updated at Posted at 2019-08-28

テストで使っている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引数のreturnsresult」は完了です。残った 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 は何千箇所もあったので、手作業では何日もかかったことでしょう。

また、使った変換は全て機械的なものだったので、バグは生じませんでした(本当にバグっていないかは、実際にテストをコンパイル・実行して確認できました)。


  1. 商用版では出来たりするかもしれませんが、私が使っていたIntelliJ IDEAでは出来ませんでした。 

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0