我在类App
中有一个方法addToClonedList(List)
,它调用类Dependency
中的方法add(List)
。 我想对addToClonedList(List)
进行单元测试,并且我想创建一个模拟Dependency
对象(称为mockDependency
)并验证是否使用正确的参数调用了方法mockDependency.add(List)
。 然而,由于clonedList
引用的List
实例在有问题的调用后添加了一个新元素(100
),Mockito认为2元素List
被传递给了mockDependency
,并且测试失败。 实际上,传递给mockDependency.add(List)
的List
在调用时只包含一个元素。 这是预期的莫米托行为吗? 有没有更标准的方法来测试我是否通过了正确的List
mockDependency.add(List)
?
请参阅下面的代码结构和测试输出:
main/java/App.java:
import java.util.*;
public class App {
Dependency dataLayer = new Dependency();
void addToClonedList(List<Integer> integerList) {
List<Integer> clonedList = new ArrayList<Integer>(integerList);
dataLayer.add(clonedList);
clonedList.add(100);
}
}
main/java/Dependency.java:
import java.util.List;
public class Dependency {
void add(List<Integer> integerList) {
//
}
}
test/java/AppTest.java:
import java.util.*;
import org.junit.*;
import org.mockito.*;
public class AppTest {
@InjectMocks
App app;
@Mock
Dependency mockDependency;
@Before
public void beforeEachTest() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testWithVerify() {
List<Integer> originalList = new ArrayList<>();
originalList.add(1);
app.addToClonedList(originalList);
Mockito.verify(mockDependency).add(Mockito.eq(originalList));
}
}
绒球.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.athenahealth</groupId>
<artifactId>mockitotest</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.testifyproject.mock</groupId>
<artifactId>mockito</artifactId>
<version>0.9.8</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
测试输出:
Argument(s) are different! Wanted:
mockDependency.add([1]);
-> at AppTest.testWithVerify(AppTest.java:25)
Actual invocation has different arguments:
mockDependency.add([1, 100]);
-> at App.addToClonedList(App.java:8)
Comparison Failure:
Expected :mockDependency.add([1]);
Actual :mockDependency.add([1, 100]);
正如Andy Turner所指出的,问题在于Mockito只保留对传递的值的引用,而不克隆它。这是有道理的,因为对于像 String 这样的不可变值对象来说,这是浪费的,而对于像 Thread 这样的某些引用来说
,这几乎是不可能的。但是,您可以编写自己的答案来自己制作副本:
List<Integer> listSnapshot;
@Test
public void testWithVerify() {
List<Integer> originalList = new ArrayList<>();
originalList.add(1);
Mockito.when(mockDependency.add(any())).thenAnswer(invocation => {
listSnapshot = new ArrayList<>((List<Integer>) (invocation.getArguments()[0]));
});
app.addToClonedList(originalList);
assertEquals(originalList, listSnapshot);
}
Mockito并不神奇。
当在模拟上调用add
方法时,模拟只是保留对参数值的引用。然后,eq
匹配器进行比较,以查看您传入verify
的参数是否等于该值,使用equals
。它不能做得比这更好,因为一般来说,它不知道如何获取参数的副本。
如果您之后修改传递到模拟中的值,Mockito 不知道您已经这样做了,并且仍然只是使用equals
比较两个引用。
你用clonedList
在模拟上调用该方法,然后在clonedList
中添加一个值,但不integerList
。因此,当您将clonedList
与使用eq
originalList
进行比较时,它们是不同的,因此检查不会通过。
正如建议的那样,如果您不添加到clonedList
,则测试通过;然后您可以看到它不是通过将eq
替换为same
来比较使用==
(然后测试将失败)。