添加附加存根时调用第一个存根
First stub is called when adding an additional stub
我有以下要测试的对象:
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()
方法进行存根,以便它 returns 一个特定参数的值,否则会抛出一个由 MyObject.readFiles
.
处理的 IOException
这里奇怪的是第二个存根 (downloader.download("testFile")
) 抛出第一个存根 (downloader.download(anyString())
) 中设置的 IOException。我已经通过在我的第一个存根中抛出一个不同的异常来验证这一点。
有人能解释一下为什么在添加额外的存根时会抛出异常吗?我认为创建存根不会调用 method/other 存根。
你的第二个模拟语句被第一个模拟语句覆盖(因为两个模拟语句都传递了一个字符串参数)。如果您想通过模拟测试涵盖尝试和追回,请编写 2 个不同的测试用例。
I thought that creating a stub does not call the method/other stubs.
这个假设是错误的,因为存根 是 调用模拟方法。你的测试方法还是很朴素java!
由于 anyString
的存根将覆盖任何特定字符串的存根,因此您将不得不编写两个测试或为两个特定参数存根:
when(downloader.download("fail")).thenThrow(new IOException());
when(downloader.download("testFile")).thenReturn("mockContent");
Mockito 是一段非常复杂的代码,它尽力让您可以编写
when(downloader.download(anyString())).thenThrow(new IOException());
意思是“when
downloader
模拟 download
方法使用 anyString
参数 thenThrow
和 IOException
调用”(即它可以从左到右阅读)。
但是,由于代码仍然很简单java,调用顺序实际上是:
String s1 = anyString(); // 1
String s2 = downloader.download(s1); // 2
when(s2).thenThrow(new IOException()); // 3
在幕后,Mockito 需要这样做:
- 为任何字符串参数注册一个
ArgumentMatcher
- 在
downloader
mock 上注册方法调用 download
,其中参数由先前注册的 ArgumentMatcher
定义
- 为之前注册的模拟方法调用注册一个动作
如果你现在打电话
... downloader.download("testFile") ...
downloader
模拟检查是否有 "testFile"
的动作寄存器(有,因为已经有任何字符串的动作)并相应地抛出 IOException
.
问题是当你写
when(downloader.download("testFile")).thenReturn("mockContent");
首先要调用的是 downloader.download
,您已经存根以抛出异常。
解决方案是使用 Mockito 提供的稍微更通用的存根语法。这种语法的优点是它在存根时不调用实际方法。
doThrow(IOException.class).when(downloader).download(anyString());
doReturn("mock content").when(downloader).download("test file");
我在 my answer here
中列出了第二种语法的其他优点
我有以下要测试的对象:
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()
方法进行存根,以便它 returns 一个特定参数的值,否则会抛出一个由 MyObject.readFiles
.
这里奇怪的是第二个存根 (downloader.download("testFile")
) 抛出第一个存根 (downloader.download(anyString())
) 中设置的 IOException。我已经通过在我的第一个存根中抛出一个不同的异常来验证这一点。
有人能解释一下为什么在添加额外的存根时会抛出异常吗?我认为创建存根不会调用 method/other 存根。
你的第二个模拟语句被第一个模拟语句覆盖(因为两个模拟语句都传递了一个字符串参数)。如果您想通过模拟测试涵盖尝试和追回,请编写 2 个不同的测试用例。
I thought that creating a stub does not call the method/other stubs.
这个假设是错误的,因为存根 是 调用模拟方法。你的测试方法还是很朴素java!
由于 anyString
的存根将覆盖任何特定字符串的存根,因此您将不得不编写两个测试或为两个特定参数存根:
when(downloader.download("fail")).thenThrow(new IOException());
when(downloader.download("testFile")).thenReturn("mockContent");
Mockito 是一段非常复杂的代码,它尽力让您可以编写
when(downloader.download(anyString())).thenThrow(new IOException());
意思是“when
downloader
模拟 download
方法使用 anyString
参数 thenThrow
和 IOException
调用”(即它可以从左到右阅读)。
但是,由于代码仍然很简单java,调用顺序实际上是:
String s1 = anyString(); // 1
String s2 = downloader.download(s1); // 2
when(s2).thenThrow(new IOException()); // 3
在幕后,Mockito 需要这样做:
- 为任何字符串参数注册一个
ArgumentMatcher
- 在
downloader
mock 上注册方法调用download
,其中参数由先前注册的ArgumentMatcher
定义
- 为之前注册的模拟方法调用注册一个动作
如果你现在打电话
... downloader.download("testFile") ...
downloader
模拟检查是否有 "testFile"
的动作寄存器(有,因为已经有任何字符串的动作)并相应地抛出 IOException
.
问题是当你写
when(downloader.download("testFile")).thenReturn("mockContent");
首先要调用的是 downloader.download
,您已经存根以抛出异常。
解决方案是使用 Mockito 提供的稍微更通用的存根语法。这种语法的优点是它在存根时不调用实际方法。
doThrow(IOException.class).when(downloader).download(anyString());
doReturn("mock content").when(downloader).download("test file");
我在 my answer here
中列出了第二种语法的其他优点