4

私たちは Mock-Factory を使用して、mockito 自体についての必要最小限のノウハウで、開発者にモッキング機能について最大限の快適さを提供しています。

そのために、私たちの Mock-Factory は、与えられたクラス名、メソッド名 (regexp による)、および次のように見える特定の戻り値 (この質問の関連部分までクリーンアップ) を指定してモックを作成するメソッドを提供しています。 ):

public <T> T getMockForMethod(Class<T> clazz, String methodName, Object methodResponse)
{
  T mockForMethod = mock(clazz);
  for (Method m : clazz.getDeclaredMethods ())
  {
    if (m.getName ().matches (methodName) && 
        m.getReturnType ().isAssignableFrom (methodResponse.getClass ()))
    {
      try
      {
         Class<?>[] paramTypes = m.getParameterTypes ();
         Object[] params = new Object[paramTypes.length];
         for (Object o : params)
         {
           o = Mockito.anyObject ();
         }
         Mockito.when (m.invoke (mockForService, params)).thenReturn (methodResponse);
      }
      catch (IllegalArgumentException e)
      {
        e.printStackTrace (System.err);
      }
      catch (IllegalAccessException e)
      {
        e.printStackTrace (System.err);
      }
      catch (InvocationTargetException e)
      {
        e.printStackTrace (System.err);
      }
    }
  }
  return mockForMethod;
}

ご覧のとおり、メソッド名は名前 (正規表現) と正しい指定された戻り値の型で一致しています。

それは正常に動作しますが、私は人工パラメータ配列を構築しなければならないという事実に少し悩まされていますparams! いいえ、アプローチ

Mockito.when (m.invoke (mockForService, Mockito.anyVararg ())).thenReturn(methodResponse);

うまくいきませんでした!でも、なぜだかよく分からない!?

上記のコードの理由またはより良い代替案を誰か教えてもらえますか?

4

3 に答える 3

8

あなたのアプローチは本当に良いものではありません. あなたのチームが「若者」であっても、Mockito を使用するときに ASM を書かなければならないわけではありません。さらに、この方法を使用すると、Mockito が提供するシンプルさ、表現力、プラグ可能性などのすべてのメリットを回避できます。建築家として、私はエンジニアをベビーパークに入れるよりも、エンジニアが何をしているかを理解してもらいたいと思っています。そうでなければ、どうすれば彼らは素晴らしいチームになることができますか?

また、ここで提供される実装は、リフレクション、ブリッジ メソッド、varargs、オーバーライドなどを処理するときに発生する可能性のあるすべてのケースをサポートするには、おそらく単純すぎるでしょう。このコードが失敗した場合、理解できるメッセージはありません。要するに、Mockito を直接使用する利点をすべて失い、とにかくプロジェクトに不要なものを追加することになります。

編集: JB Nizet の回答を見たところ、彼に完全に同意します。


しかし、あなたの質問に答えるために、そこで何が起こっているのか. コードを簡単に見てみると、メソッドに渡される引数を気にしたくないようです。

したがって、モックされているクラスに次の実際のメソッドがあるとします。

String log2(String arg1, String arg2)

String log1N(String arg1, String... argn)

log2これで、コンパイラが認識するのは、型の 2 つのパラメーターを受け取る最初のメソッドと、型の 1 つと型のもう 1つの 2 つのパラメーターを受け取るメソッドですString(変数引数は、コンパイラーによって配列に変換されます)。log1NStringString[]

これらのメソッドで Mockito を直接使用する場合は、次のように記述します。

given(mock.log2("a", "b")).will(...);
given(mock.log1N("a", "b", "c", "d")).will(...);

logN("a", "b", "c", "d")普通の Java と同じように記述します。引数マッチャーを使用したい場合は、これを 2 arg メソッドで記述します。

given(mock.log2(anyString(), anyString())).will(...);

そして今、可変引数メソッドを使用します:

given(mock.log1N(anyString(), anyString(), anyString())).will(...); // with standard arg matchers
given(mock.log1N(anyString(), Mockito.<String>anyVararg())).will(...); // with var arg matcher

最初のケースでは、Mockito は、最後の 2 つの引数マッチャーが最後の vararg に入る必要があることを理解するのに十分スマートですargnanyVarargmockito、任意の数の引数が存在する可能性があります。

さて、リフレクション コードに戻ると、の署名Method.invokeは次のとおりです。

public Object invoke(Object obj, Object... args)

実引数を渡すときのリフレクションと可変引数の一般的な使用法は次のとおりです。

log2_method.invoke(mock, "a", "b");
log1N_method.invoke(mock, "a", new String[] { "b", "c", "d" });

または、この呼び出しメソッドは vararg に基づいているため、次のように記述できます。

log1N_method.invoke(mock, new Object[] {"a", new String[] { "b", "c", "d" }});

したがって、invoke で渡された引数 vararg 配列は、呼び出されたメソッドのシグネチャと実際に一致する必要があります。

もちろん、この呼び出しは失敗し、log1N_method.invoke(mock, "a", "b", "c", "d");

したがって、このコード行を で試したときanyVararg、呼び出しは呼び出されたメソッド引数の署名を尊重していませんでした:

Mockito.when (m.invoke(mockForMethod, Mockito.anyVararg())).thenReturn(methodResponse);

メソッドmに引数が 1 つしかない場合にのみ機能します。それでも、配列内にあるリフレクション API に到達する必要があります (vararg は実際には配列であるため)。ここでのトリックは、 の vararg がinvoke(Object obj, Object... args)呼び出されたメソッドの vararg と混同していることです。

したがって、私の例で引数マッチャーを使用すると、次のようにする必要があります。

when(
    log1N.invoke(mock, anyString(), new String[] { Mockito.<String>anyVararg() })
).thenReturn("yay");

したがって、vararg である引数が 1 つしかない場合、それは同じことです。

String log1(String... argn)

when(
    logN.invoke(mock, new String[] { Mockito.<String>anyVararg() })
).thenReturn("yay");

anyVarargもちろん、署名の引数レイアウトが一致しないため、可変引数以外のメソッドでは使用できません。

ここでわかるように、この方法で Mockito をチームに抽象化すると、多くのクラス レベルの奇妙さを管理する必要があります。これが不可能だと言っているのではありません。しかし、このコードの所有者として、コードを維持し、修正し、問題が発生する可能性があることを考慮して、この抽象化コードのユーザーが理解できるようにする必要があります。

強引に感じて申し訳ありませんが、それは私には間違っているように思われるので、これらの警告を強調します.

于 2013-04-12T13:50:29.543 に答える