Mockito.eq 匹配器似乎与列表引用匹配,而不是值匹配

Aar*_*ron 8 java unit-testing reference list mockito

我在类addToClonedList(List)中有一个方法App调用add(List)类中的方法Dependency。我想要进行单元测试addToClonedList(List),并且想要创建一个模拟Dependency对象(称为)并验证是否使用正确的参数调用mockDependency该方法。mockDependency.add(List)但是,由于List引用的实例在相关调用之后clonedList将一个新元素 ( 100) 添加到 if ,因此 Mockito 认为 2 元素List已传递给mockDependency,并且测试失败。实际上,调用时List传递给的仅包含一个元素。mockDependency.add(List)这是预期的 Mockito 行为吗?有没有更标准的方法来测试我已经通过了正确的List方法mockDependency.add(List)

请参阅下面的代码结构和测试输出:

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

主/java/Dependency.java:

import java.util.List;

public class Dependency {
  void add(List<Integer> integerList) {
    //
  }
}
Run Code Online (Sandbox Code Playgroud)

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

pom.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>
Run Code Online (Sandbox Code Playgroud)

测试输出:

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

Jef*_*ica 4

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