11

非常に単純な Mockito 実行 JUnit テストとクラスでは、Java 1.6.0_32 と Java 1.7.0_04 でテストを実行すると異なる出力が表示され、なぜこれが発生するのかを理解したいと考えています。ある種の消去が行われていると思われますが、決定的な答えが欲しいです。

コマンドラインから実行する方法のサンプルコードと手順は次のとおりです。

FooServiceTest.java

import org.junit.*;
import org.junit.runner.*;
import org.mockito.*;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.*;
import java.util.*;

@RunWith(MockitoJUnitRunner.class)
public class FooServiceTest {
  @Mock Map<String, String> mockStringString;
  @Mock Map<String, Integer> mockStringInteger;

  @InjectMocks FooService fooService;

  public static void main(String[] args) {
    new JUnitCore().run(FooServiceTest.class);
  }

  @Before
  public void setup() {
    MockitoAnnotations.initMocks(this);
  }

  @Test
  public void checkInjection() {
    when(mockStringString.get("foo")).thenReturn("bar");
    fooService.println();
  }
}

FooService.java

import java.util.*;

public class FooService {
  private Map<String, String> stringString = new HashMap<String, String>();
  private Map<String, Integer> stringInteger = new HashMap<String, Integer>();

  public void println() {
    System.out.println(stringString.get("foo") + " " + stringInteger);
  }
}

この例をコンパイルして実行するには:

  • 上記をファイルに保存します
  • junit.4.10.jarmockito-all-1.9.0.jarをダウンロードして同じディレクトリに配置します。
  • JDKを含むようにPATHを設定します
  • でコンパイルjavac -cp junit-4.10.jar;mockito-all-1.9.0.jar *.java
  • で実行java -cp .;junit-4.10.jar;mockito-all-1.9.0.jar FooServiceTest

上記の出力はnull {}@InjectMocksフィールド インジェクションが両方ともタイプ Map であるため、タイプを正しく解決できないためだと思います。これは正しいですか?

モック名の 1 つをクラスのフィールドと一致するように変更すると、Mockito が一致を検出できるようになります。たとえば、

@Mock Map<String, Integer> mockStringInteger;

@Mock Map<String, Integer> stringInteger;

次に、Java 1.6.0_32でコンパイル/実行すると(IMHOが予想される)出力bar stringIntegerが得られますが、1.7.0_04ではnull stringInteger.

これを実行する方法は次のとおりです(Windows 7のコマンドラインから):

E:\src\mockito-test>set PATH="C:\Program Files (x86)\Java\jdk1.6.0_32\bin"
E:\src\mockito-test>javac -cp junit-4.10.jar;mockito-all-1.9.0.jar *.java
E:\src\mockito-test>java -cp .;junit-4.10.jar;mockito-all-1.9.0.jar FooServiceTest
    bar stringInteger
E:\src\mockito-test>set PATH="C:\Program Files (x86)\Java\jdk1.7.0_04\bin"
E:\src\mockito-test>javac -cp junit-4.10.jar;mockito-all-1.9.0.jar *.java
E:\src\mockito-test>java -cp .;junit-4.10.jar;mockito-all-1.9.0.jar FooServiceTest
    null stringInteger
4

3 に答える 3

5

上記の出力は I null {} であると思います。これは、@InjectMocks フィールド インジェクションは両方とも Map 型であるため、型を正しく解決できないためです。これは正しいです?

はい、これらのフィールドを修正してください。Mockito はあいまいさを解消できないため、これらのあいまいなフィールドは単純に無視されます。

非常に単純な Mockito 実行 JUnit テストとクラスでは、Java 1.6.0_32 と Java 1.7.0_04 でテストを実行すると異なる出力が表示され、なぜこれが発生するのかを理解したいと考えています。

実際には、JDK 6 と JDK 7 では Arrays.sort の動作が異なるため、Collections.sort() の動作が異なります。違いは、20% 少ないスワップを実行する新しいアルゴリズムにあります。これはおそらく、JDK6 と JDK7 で動作するようになったこのスワップ操作です。

同じ型 (または同じ消去) を持つフィールドの 1 つだけのモック フィールドの名前を変更する場合、「問題を探している」可能性があります。モックをタイプで区別できない場合、実際にはすべてのモック フィールドに対応するフィールドの名前を付ける必要がありますが、Javadoc には明確に記載されていません。

ところで、その奇妙な動作を報告してくれてありがとう、私はMockitoで問題を作成しましたが、今のところ、この問題を実際に解決するのではなく、JDK 全体で同じ動作を保証します。この状況を解決するには、互換性を維持しながら新しいアルゴリズムをコーディングする必要があるかもしれません。それまでの間、テストされたクラスのフィールドに応じてすべてのフィールド モックに名前を付ける必要があります。

今のところ、JDK6 と JDK7 で同じ順序を適用するために、追加の比較を使用してコンパレーターを微調整する必要があります。さらに、Javadoc にいくつかの警告を追加します。

編集: 2 つのパスを作成すると、ほとんどの人の問題が解決する可能性があります。

それが役立つことを願っています。問題を見つけてくれてありがとう。


また、どちらかMockitoAnnotations.initMocks(this);または runnerが必要ですが@RunWith(MockitoJUnitRunner.class)、両方を使用する必要はなく、問題が発生する可能性さえあります。:)

于 2012-06-29T19:30:27.863 に答える
2

注入されるフィールドの 1 つに一致するモックが複数ある場合、Mockito の動作は未定義です。ここで、「一致」とは、型パラメーターを無視して正しい型であることを意味します。型の消去により、Mockito は型パラメーターを認識できなくなります。したがって、あなたの例では、2 つのモックのいずれかを 2 つのフィールドのいずれかに注入できます。

Java 6 で Java 7 とは異なる動作を観察できたという事実は、ちょっとしたニシンです。Java のどちらのバージョンでも、Mockito が挿入する2 つのフィールドのいずれかに対して、mockStringStringまたはを正しく選択することを期待する理由はありません。mockStringInteger

于 2012-05-26T23:10:52.533 に答える
0

これは正しいですか?

確かに、型消去のために、Mockitoは実行時/反射を介してさまざまなマップ間の違いを確認できません。これにより、Mockitoは適切な注入を行うのに苦労しています。

に対するnull応答にstringString.get("foo")は、次の2つの原因が考えられます。

  1. stringStringは適切にモックされましたが、スタブは実行されませんでした(get常にreturn null)。
  2. stringStringはモックされていないためHashMap、の値がないため"foo"getが返されnullます。

変数への{}応答はstringInteger、実際の(空の)で(クラス内で)初期化されたことを意味しますHashMap

つまり、出力からわかるのstringIntegerは、モックされていないということです。どうstringStringですか?

2つ@Mockのいずれにも、テスト対象のクラスのフィールドのいずれにも一致する名前がない場合、何もモックされません。理由?どのフィールドに注入するかを決定できないのではないかと思うので、あざけることはありません。これを確認するには、両方の変数を表示します。これにより、両方が生成され{}ます。これはあなたのnull価値を説明しています。

の1つに@Mock一致する名前があり、もう1つに一致しない名前がある場合(変更、1つとmockedStringString1つstringInteger、つまり同じ名前)、Mockitoは何をすべきですか?

実行したいのは、そのうちの1つだけを、対応する名前のフィールドにのみ挿入することです。あなたの場合、あなたはmockedStringString(これが一致しないことを期待するでしょう)そしてstringInteger(あなたはそれが一致することを期待するでしょう)を持っています。mockedStringString一致しない(!)をスタブしたので、期待される結果はになりますnull

言い換えれば、与えられた特定の例では、Java 7の応答はOKであり、Java6の応答はOKではないと思います。

Java 6で得られる(予期しない)動作で何が起こっているかを確認するには、1つだけにしてみてください。@Mock適切にモックし、のモックstringStringないstringInteger場合は、のモックがフィールドstringStringに挿入されstringIntegerます。言い換えると、Mockitoは最初に(名前を付けて)注入できることを理解し、次に一致する可能性の1つ(ただし必ずしも正しいものではない)にモックを注入するようです。

于 2012-05-26T09:49:58.817 に答える