4

次のメソッド シグネチャを含むインターフェイスをモックしたいとします。Mockito

public void doThis(Object o);

public void doThis(Object... o)

(他のメソッドではなく) 正確に 1 回呼び出されたことを確認する必要があります。doThis(Object o)

最初に、次の行でうまくいくと思いました。

verify(mock, times(1)).doThis(anyObject());

ただし、これは Windows では機能するようですが、Linux では機能しません。この環境では、他のdoThisメソッドの呼び出しが想定されているためです。
これは、anyObject()引数が両方のメソッド シグネチャに一致するように見え、一方が多かれ少なかれ予測できない方法で選択されるために発生します。

Mockito が常に検証を選択doThis(Object o)するように強制するにはどうすればよいですか?

4

2 に答える 2

2

これは、mockito の問題ではありません。

さらに調査しているうちに、実際のメソッドはコンパイル時に選択されることに気付きました (JLS §15.12.2)。したがって、基本的にクラスファイルはWindowsとLinuxで異なり、これによりmockitoの動作が異なりました。

インターフェイスは推奨されません (「参考文献」を参照Effective Java, 2nd Edition, Item 42)。以下のように変更しました。

public void doThis(Object o);

public void doThis(Object firstObj, Object... others)

この変更により、予想される (最初の) メソッドが常に選択されます。

まだ残っていることが 1 つあり
ます。Windows の Java コンパイラ (Eclipse コンパイラ) は、Linux の Oracle JDK javac とは異なる出力を生成するのはなぜですか?

ここでは Java 言語仕様がかなり厳密であると予想されるため、これは ECJ のバグである可能性があります。

于 2015-12-15T11:13:36.480 に答える
2

他の回答のほとんどに同意しますが、まだ回答されていない部分が 1 つあります。コンパイラの違いはなぜですか?

よく見ると、これは ecj と javac の違いではなく、JLS の異なるバージョン間の違いです。

  • コンプライアンス 1.7 以下でコンパイルすると、両方のコンパイラが最初の (単一の引数) メソッドを選択します。
  • コンプライアンス 1.8 でコンパイルすると、両方のコンパイラが 2 番目の (varargs) メソッドを選択します。

生成されたバイトコードを見てみましょう。

     7: invokestatic  #8                  // Method anyObject:()Ljava/lang/Object;
    10: checkcast     #9                  // class "[Ljava/lang/Object;"
    13: invokevirtual #10                 // Method doThis:([Ljava/lang/Object;)V

(両方のコンパイラもこれらのバイトに同意します)

つまり、Java 8 の推論はより強力になり、 to の型引数を推論できるようになりましanyObject()Object[]checkcastこれは、ソース コードに変換された次の命令で確認できます(Object[])anyObject()。これは、 もdoThis(Object...)varargs マジックなしで呼び出すことができることを意味しますが、 type の引数を 1 つ渡すだけObject[]です。

これで、両方のメソッドが同じカテゴリ (「固定アリティ呼び出しによって適用可能」) で適用可能になり、適用可能なメソッドの中で最も具体的なものを検索すると、2 番目のメソッドが選択されます。

対照的に、Java 7 では 2 番目のメソッドの呼び出しは可変アリティ呼び出しとしてのみ許可されますが、固定アリティの一致が見つかった場合でも、これは試行されません。

上記は、JLS の変更に対してプログラムを堅牢にする方法も示しています。

verify(mock, times(1)).doThis(Matchers.<Object>anyObject());

これにより、すべてのバージョンのすべてのコンパイラに最初のメソッドを選択するように指示されます。これは、常にdoThis()asの引数が表示されるObjectためです。この不健康なオーバーロードを本当に回避できない場合、つまり :)

于 2015-12-16T21:27:32.083 に答える