@InjectMocks与Java 6和7的行为不同

and*_*dyb 11 java type-erasure mockito java-6 java-7

使用非常简单的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();
  }
}
Run Code Online (Sandbox Code Playgroud)

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

要编译并运行此示例:

  • 将上述内容保存到文件中
  • 下载并放入同一目录junit.4.10.jarmockito-all-1.9.0.jar
  • 设置PATH以包含JDK
  • 编译 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类型.这是对的吗?

现在更改其中一个模拟名称以匹配类中的字段应该允许Mockito找到匹配项.例如改变

@Mock Map<String, Integer> mockStringInteger;
Run Code Online (Sandbox Code Playgroud)

@Mock Map<String, Integer> stringInteger;
Run Code Online (Sandbox Code Playgroud)

然后用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
Run Code Online (Sandbox Code Playgroud)

Bri*_*ice 5

我相信上面的输出是I null {},因为@InjectMocks字段注入无法正确解析类型,因为它们都是Map类型.它是否正确?

是的,在这些领域正确Mockito无法消除歧义,所以它只是忽略了这些模糊的领域.

使用非常简单的Mockito运行JUnit测试和类,当使用Java 1.6.0_32和Java 1.7.0_04运行测试时,我看到不同的输出,并且想要理解为什么会发生这种情况.

实际上,差异在于Arrays.sort的不同行为,因此在JDK 6和JDK 7之间存在Collections.sort().差异在于新的算法,它应该执行少于掉期20%的交换.这可能是这个交换操作使得JDK6和JDK7下的工作.

如果你只重命名具有相同类型(或相同擦除)的字段的一个模拟字段,那么我可能正在"寻找麻烦".当模拟不能按类型区分时,你真的应该将所有模拟字段命名为相应的字段,但Javadoc并没有明确说明.

非常感谢报告这种奇怪的行为,我在Mockito上创建了一个问题,但是现在我不会真正解决这个问题,而是确保JDK中的相同行为.解决这种情况可能需要在保持兼容性的同时编写新的algorythm,同时您应该将所有的场模拟命名为测试类的字段.

目前要做的事情可能是通过额外的比较来调整比较器,以在JDK6和JDK7上强制执行相同的顺序.另外在Javadoc中添加了一些警告.

编辑:两次通过可能会解决大多数人的问题.

希望有所帮助.感谢您发现问题.


另外,无论是你需要的MockitoAnnotations.initMocks(this);还是跑步者@RunWith(MockitoJUnitRunner.class),都不需要使用两者,甚至可能会引起一些问题.:)

  • @andyb问题在trunk中修复:) (2认同)