如何仅在 x:th 调用中 return 模拟项目?
How do I return mocked item only on the x:th call?
我在使用 PowerMock 时遇到了 运行 问题,如果我只想 return 一次模拟项目,我会被迫创建难看的代码。
例如,我有以下代码:
mockMap = spy(new HashMap());
HashMap<String, String> normalMap = new HashMap<>();
HashMap<String, String> normalMap2 = new HashMap<>();
HashMap<String, String> normalMap3 = new HashMap<>();
whenNew(HashMap.class).withNoArguments()
.thenReturn(normalMap)
.thenReturn(mockMap)
.thenReturn(normalMap2)
.thenReturn(normalMap3);
这当然有效,但感觉很笨拙,尤其是因为我需要为每个新调用创建一个新的哈希图。
所以我的问题是:有没有办法告诉 PowerMock 它应该在调用一定数量后停止干扰?
编辑:
阅读答案后,我得到以下信息:
final AtomicInteger count = new AtomicInteger(0);
whenNew(HashMap.class).withNoArguments().thenAnswer(invocation -> {
switch (count.incrementAndGet())
{
case 1:
return mockMap;
default:
return new HashMap<String, String>();
}
});
但是它使用以下代码给我一个 WhosebugError:
describe("test", () -> {
beforeEach(() -> {
mockStatic(HashMap.class);
final AtomicInteger count = new AtomicInteger(0);
whenNew(HashMap.class).withNoArguments().thenAnswer(invocation ->
{
switch (count.incrementAndGet())
{
case 5:
return mockMap;
default:
return new HashMap<String, String>();
}
});
});
it("Crashes on getting a new HashMap", () -> {
HashMap map = new HashMap<>();
map.put("test", "test");
HashMap normal = new HashMap<String, String>();
expect(normal.containsValue("test")).toBeTrue();
});
});
值得注意的是,我在较大的测试中没有 mockStatic(HashMap.class) 得到相同的错误(如果我删除 mockStatic 调用,我就摆脱了)
一个有效的解决方案(但感觉像是一种变通方法)正在通过将默认语句编辑为以下内容来构建:
default:
normalMap.clear();
return normalMap;
您可以将 thenReturn 与多个参数一起使用,如下所示:
whenNew(HashMap.class).withNoArguments()
.thenReturn(normalMap, mockMap, normalMap2, normalMap3);
或者像这样写你自己的Answer:
whenNew(HashMap.withNoArguments()).doAnswer(new Answer() {
private int count = 0;
public Object answer(InvocationOnMock invocation) {
// implement your logic
// if (count ==0) etc.
}
});
你可以使用 Mockito 的 org.mockito.stubbing.Answer
:
final AtomicInteger count = new AtomicInteger(0);
PowerMockito.whenNew(HashMap.class).withNoArguments()
.thenAnswer(new Answer<HashMap<String, String>>() {
@Override
public HashMap<String, String> answer(InvocationOnMock invocation) throws Throwable {
count.incrementAndGet();
switch (count.get()) {
case 1: // first call, return normalMap
return normalMap;
case 2: // second call, return mockMap
return mockMap;
case 3: // third call, return normalMap2
return normalMap2;
default: // forth call (and all calls after that), return normalMap3
return normalMap3;
}
}
});
请注意,我必须使用 java.util.concurrent.atomic.AtomicInteger
并将其声明为 final
变量,原因有两个:
- 匿名内部使用的变量 class 必须是最终的 (because Java defined it that way)
- 如果我创建一个
final int
,我不能count++
注意:在此解决方案中,您还必须将所有 normalMap
更改为 final
实际上,如果你所有的 normalMap
都一样,你可以这样做:
PowerMockito.whenNew(HashMap.class).withNoArguments()
.thenAnswer(new Answer<HashMap<String, String>>() {
@Override
public HashMap<String, String> answer(InvocationOnMock invocation) throws Throwable {
count.incrementAndGet();
if (count.get() == 2) { // second call
return mockMap;
}
return normalMap; // don't forget to make normalMap "final"
// or if you prefer: return new HashMap<String, String>();
}
});
PS:如所述,除了使用AtomicInteger
,您还可以创建一个int
计数器在匿名 class 中(由您选择,因为两者都有效):
PowerMockito.whenNew(HashMap.class).withNoArguments()
.thenAnswer(new Answer<HashMap<String, String>>() {
private int count = 0;
@Override
public HashMap<String, String> answer(InvocationOnMock invocation) throws Throwable {
count++;
if (count == 2) { // second call
return mockMap;
}
return normalMap; // don't forget to make normalMap "final"
// or if you prefer: return new HashMap<String, String>();
}
});
这也适用于 switch
解决方案。
我在使用 PowerMock 时遇到了 运行 问题,如果我只想 return 一次模拟项目,我会被迫创建难看的代码。 例如,我有以下代码:
mockMap = spy(new HashMap());
HashMap<String, String> normalMap = new HashMap<>();
HashMap<String, String> normalMap2 = new HashMap<>();
HashMap<String, String> normalMap3 = new HashMap<>();
whenNew(HashMap.class).withNoArguments()
.thenReturn(normalMap)
.thenReturn(mockMap)
.thenReturn(normalMap2)
.thenReturn(normalMap3);
这当然有效,但感觉很笨拙,尤其是因为我需要为每个新调用创建一个新的哈希图。
所以我的问题是:有没有办法告诉 PowerMock 它应该在调用一定数量后停止干扰?
编辑: 阅读答案后,我得到以下信息:
final AtomicInteger count = new AtomicInteger(0);
whenNew(HashMap.class).withNoArguments().thenAnswer(invocation -> {
switch (count.incrementAndGet())
{
case 1:
return mockMap;
default:
return new HashMap<String, String>();
}
});
但是它使用以下代码给我一个 WhosebugError:
describe("test", () -> {
beforeEach(() -> {
mockStatic(HashMap.class);
final AtomicInteger count = new AtomicInteger(0);
whenNew(HashMap.class).withNoArguments().thenAnswer(invocation ->
{
switch (count.incrementAndGet())
{
case 5:
return mockMap;
default:
return new HashMap<String, String>();
}
});
});
it("Crashes on getting a new HashMap", () -> {
HashMap map = new HashMap<>();
map.put("test", "test");
HashMap normal = new HashMap<String, String>();
expect(normal.containsValue("test")).toBeTrue();
});
});
值得注意的是,我在较大的测试中没有 mockStatic(HashMap.class) 得到相同的错误(如果我删除 mockStatic 调用,我就摆脱了)
一个有效的解决方案(但感觉像是一种变通方法)正在通过将默认语句编辑为以下内容来构建:
default:
normalMap.clear();
return normalMap;
您可以将 thenReturn 与多个参数一起使用,如下所示:
whenNew(HashMap.class).withNoArguments()
.thenReturn(normalMap, mockMap, normalMap2, normalMap3);
或者像这样写你自己的Answer:
whenNew(HashMap.withNoArguments()).doAnswer(new Answer() {
private int count = 0;
public Object answer(InvocationOnMock invocation) {
// implement your logic
// if (count ==0) etc.
}
});
你可以使用 Mockito 的 org.mockito.stubbing.Answer
:
final AtomicInteger count = new AtomicInteger(0);
PowerMockito.whenNew(HashMap.class).withNoArguments()
.thenAnswer(new Answer<HashMap<String, String>>() {
@Override
public HashMap<String, String> answer(InvocationOnMock invocation) throws Throwable {
count.incrementAndGet();
switch (count.get()) {
case 1: // first call, return normalMap
return normalMap;
case 2: // second call, return mockMap
return mockMap;
case 3: // third call, return normalMap2
return normalMap2;
default: // forth call (and all calls after that), return normalMap3
return normalMap3;
}
}
});
请注意,我必须使用 java.util.concurrent.atomic.AtomicInteger
并将其声明为 final
变量,原因有两个:
- 匿名内部使用的变量 class 必须是最终的 (because Java defined it that way)
- 如果我创建一个
final int
,我不能count++
注意:在此解决方案中,您还必须将所有 normalMap
更改为 final
实际上,如果你所有的 normalMap
都一样,你可以这样做:
PowerMockito.whenNew(HashMap.class).withNoArguments()
.thenAnswer(new Answer<HashMap<String, String>>() {
@Override
public HashMap<String, String> answer(InvocationOnMock invocation) throws Throwable {
count.incrementAndGet();
if (count.get() == 2) { // second call
return mockMap;
}
return normalMap; // don't forget to make normalMap "final"
// or if you prefer: return new HashMap<String, String>();
}
});
PS:如AtomicInteger
,您还可以创建一个int
计数器在匿名 class 中(由您选择,因为两者都有效):
PowerMockito.whenNew(HashMap.class).withNoArguments()
.thenAnswer(new Answer<HashMap<String, String>>() {
private int count = 0;
@Override
public HashMap<String, String> answer(InvocationOnMock invocation) throws Throwable {
count++;
if (count == 2) { // second call
return mockMap;
}
return normalMap; // don't forget to make normalMap "final"
// or if you prefer: return new HashMap<String, String>();
}
});
这也适用于 switch
解决方案。