我有以下要测试的对象:

   public class MyObject {

    @Inject
    Downloader downloader;

    public List<String> readFiles(String[] fileNames) {
        List<String> files = new LinkedList<>();
        for (String fileName : fileNames) {
            try {
                files.add(downloader.download(fileName));
            } catch (IOException e) {
                files.add("NA");
            }
        }
        return files;
    }
}

这是我的测试:
@UseModules(mockTest.MyTestModule.class)
@RunWith(JukitoRunner.class)
public class mockTest {

    @Inject Downloader downloader;
    @Inject MyObject myObject;

    private final String[] FILE_NAMES = new String[] {"fail", "fail", "testFile"};
    private final List<String> EXPECTED_FILES = Arrays.asList("NA", "NA", "mockContent");

    @Test
    public void testException() throws IOException {
        when(downloader.download(anyString()))
                .thenThrow(new IOException());

        when(downloader.download("testFile"))
                .thenReturn("mockContent");

        assertThat(myObject.readFiles(FILE_NAMES))
                .isEqualTo(EXPECTED_FILES);
    }

    public static final class MyTestModule extends TestModule {
        @Override
        protected void configureTest() {
            bindMock(Downloader.class).in(TestSingleton.class);
        }
    }
}

我正在覆盖特定参数的anyString()匹配器。我对download()方法存根,以便它返回特定参数的值,否则抛出IOException,该异常由MyObject.readFiles处理。

奇怪的是,第二个存根(downloader.download("testFile"))会在第一个存根(downloader.download(anyString()))中抛出IOException集。我通过在第一个存根中引发另一个异常来验证这一点。

有人可以解释一下为什么添加其他存根时会引发异常吗?我以为创建存根不会调用方法/其他存根。

最佳答案

我以为创建存根不会调用方法/其他存根。

这个假设是错误的,因为存根调用了模拟方法。您的测试方法仍然是纯Java!

由于对anyString进行存根将覆盖任何特定字符串的存根,因此您将不得不编写两个测试或对两个特定参数进行存根:

when(downloader.download("fail")).thenThrow(new IOException());

when(downloader.download("testFile")).thenReturn("mockContent");

Mockito是一段非常复杂的代码,它会尽力而为,以便您可以编写
when(downloader.download(anyString())).thenThrow(new IOException());

这意味着“whendownloader的模拟download方法通过anyString参数thenThrowIOException进行调用”(即,可以从左到右读取)。

但是,由于代码仍然是纯Java,因此调用顺序实际上是:
String s1 = anyString(); // 1
String s2 = downloader.download(s1); // 2
when(s2).thenThrow(new IOException()); // 3

在后台,Mockito需要执行以下操作:
  • 为任何字符串参数
  • 注册ArgumentMatcher
  • download模拟上注册方法调用downloader,其中参数由先前注册的ArgumentMatcher
  • 定义
  • 为模拟
  • 上先前注册的方法调用注册一个操作

    如果您现在打电话
     ... downloader.download("testFile") ...
    
    downloader模拟检查是否存在"testFile"的操作寄存器(因为已经存在任何String的操作,所以存在),并因此抛出IOException

    07-25 23:58