Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
0
Help us understand the problem. What is going on with this article?
@tonluqclml

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

More than 1 year has passed since last update.

テストで使っている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では出来ませんでした。 

0
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
tonluqclml
エムスリーでソフトウェアエンジニアしています。仕事ではRubyもScalaもPythonもBashもなんでもやる雑食系。 Twitter:https://twitter.com/doloopwhile 昔の個人ブログ:http://doloopwhile.hatenablog.com/ 勤務先ブログ: https://www.m3tech.blog/
m3dev
インターネット、最新IT技術を活用し日本・世界の医療を改善することを目指します

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
0
Help us understand the problem. What is going on with this article?