使用非常简单的Mockito运行JUnit测试和类我在使用Java 1.6.0_32和Java 1.7.0_04运行测试时看到不同的输出,并且想要了解为什么这种情况正在发生。我怀疑有一些类型的擦除正在进行,但想要一个明确的答案。
以下是我的示例代码和有关如何从命令行运行的说明:
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();
}
}
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);
}
}
编译并运行此示例:
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;
到
@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
答案 0 :(得分: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()。差异在于新的algorythm应该执行20%以下的交换。这可能是这种交换操作使得JDK6和JDK7下的工作。
如果您只重命名具有相同类型(或相同擦除)的字段的一个模拟字段,那么我可能正在“寻找麻烦”。当模拟无法按类型区分时,你真的应该将所有模拟字段命名为相应的字段,但是Javadoc并没有明确说明。
非常感谢报告这种奇怪的行为,我创建了一个issue on Mockito,但是现在我不会真正解决这个问题,而是确保JDK中的相同行为。解决这种情况可能需要在保持兼容性的同时编写新的算法,同时您应该将所有的场模拟命名为测试类的字段。
目前要做的事情可能是通过额外的比较调整比较器,以在JDK6和JDK7上强制执行相同的顺序。另外在Javadoc中添加一些警告。
编辑:两次通过可能会解决大多数人的问题。
希望有所帮助。感谢您发现问题。
另外,您需要MockitoAnnotations.initMocks(this);
或跑步者@RunWith(MockitoJUnitRunner.class)
,因此不必使用两者,甚至可能会导致一些问题。 :)
答案 1 :(得分:2)
Mockito的行为是未定义的,如果有多个模拟匹配将要注入的一个字段。这里,“匹配”意味着它是正确的类型,忽略任何类型参数 - 类型擦除阻止Mockito知道类型参数。因此,在您的示例中,两个模拟中的任何一个都可以注入两个字段中的任何一个。
您已经设法观察Java 7中Java 6的不同行为这一事实是一个红色的鲱鱼。在任何一个Java版本中,没有理由期望Mockito在mockStringString
或mockStringInteger
之间正确选择它注入的两个字段中的任何一个。
答案 2 :(得分:0)
这是正确的吗??
事实上,由于type erasure,Mockito无法在运行时/通过反射看到各种地图之间的差异,这使得Mockito很难进行正确的注射。
null
对stringString.get("foo")
的回复可能有两个原因:
get
始终返回null
)。HashMap
仍然没有"foo"
的值,因此get
将返回null
。对{}
变量的stringInteger
响应表示已使用实际(空)HashMap
初始化(在课堂中)。
所以你的输出告诉你的是stringInteger
没有被嘲笑。那么stringString
呢?
如果两个@Mock
中没有一个名称与被测试类中的任何字段匹配,则不会嘲笑任何内容。原因?我怀疑它无法决定注入哪个字段,所以它不做任何嘲弄。您可以通过显示两个变量来验证这一点,这两个变量都会产生{}
。这解释了您的null
值。
如果其中一个@Mock
的名称匹配,另一个名称不匹配(您的修改,一个mockedStringString
和一个stringInteger
,即拥有同名),Mockito应该怎么做?
你想要做的只是注入其中一个,并且仅在具有相应名称的字段中。在您的情况下,您有mockedStringString
(您希望这不匹配)和stringInteger
(您希望它匹配)。由于您存在mockedStringString
(!),但不匹配,预期结果为null
。
换句话说,我认为Java 7的响应是正常的,而Java 6的响应是不行的,对于给出的具体示例。
要了解您为Java 6获得的(意外)行为发生了什么,请尝试只使用一个@Mock
- 如果我正确模拟stringString
并且没有 stringInteger
的模拟,stringString
的模拟被注入stringInteger
字段。换句话说,Mockito似乎首先想出它可以注入(给定名称),然后将模拟注入其中一个匹配的可能性(但不一定是正确的)。