Mockito:如何在调用时将 void 方法存根到 运行 一些代码
Mockito: how to stub void methods to run some code when called
我想存根存储库 class 以测试另一个具有存储库的 class(持有人 class)。 repository接口支持CRUD操作,方法很多,但是我在Holderclass上的单元测试只需要调用其中两个即可。存储库界面:
public interface IRepo {
public void remove(String... sarr);
public void add(String... sarr);
//Lots of other methods I don't need now
}
我想创建一个可以存储实例的存储库模拟,仅为 add
和 remove
定义逻辑,并提供一种检查存储在其中的方法在调用添加和删除之后。
如果我这样做:
IRepo repoMock = mock(IRepo.class);
然后我有一个哑对象,它对每个方法都不做任何事情。没关系,现在我只需要定义添加和删除的行为。
我可以创建一个 Set<String>
并仅存根这两种方法以在集合上工作。然后我将实例化一个具有 IRepo 的持有人,注入部分存根的模拟,并在行使持有人之后,检查集合以验证它包含它应该包含的内容。
我已经设法使用已弃用的方法 stubVoid
:
部分存根了 remove
之类的无效方法
Set<String> mySet = new HashSet<>();
stubVoid(repoMock).toAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
String[] stringsToDelete = (String[]) args[0];
mySet.removeAll(Arrays.asList(stringsToDelete));
return null;
}
}).on().remove(Matchers.<String>anyVararg());
但已被弃用,它并不比为 IRepo 创建部分实现好多少。有没有更好的方法?
注意:Java 请只回答 7 个答案,这应该 运行 在 Android.
假设你有
public class IFace {
public void yourMethod() {
}
}
然后要模拟它你需要
IFace mock = Mockito.mock(IFace.class);
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
//PUT YOUR CODE HERE
return null;
}
}).when(mock).yourMethod();
您可以使用
Mockito.doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
//DO SOMETHING
return null;
}
}).when(...).remove(Matchers.<String>anyVararg());
来自 Javadoc:
Use doAnswer() when you want to stub a void method with generic
Answer.
Stubbing voids requires different approach from Mockito.when(Object)
because the compiler does not like void methods inside brackets...
Example:
doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Mock mock = invocation.getMock();
return null;
}}).when(mock).someMethod();
请参阅 Mockito 的 javadoc 中的示例
如果您真的不喜欢 return null
在 Answer
的实现中,您可以创建自己的 Answer 实现,委托给 void 方法:
public abstract class DoesSomethingAnswer implements Answer<Void> {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
doSomething(invocation);
return null;
}
protected abstract void doSomething(InvocationOnMock invocation);
}
那么你的测试就少了一行:
Set<String> mySet = new HashSet<>();
Mockito.doAnswer(new DoesSomethingAnswer() {
@Override
protected void doSomething(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
String[] stringsToDelete = (String[]) args[0];
mySet.removeAll(Arrays.asList(stringsToDelete));
}
}).when(repoMock).remove(Matchers.<String> anyVararg());
或者,如果您只需要调用中的参数:
public abstract class DoesSomethingAnswer implements Answer<Void> {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
doSomething(invocation.getArguments());
return null;
}
protected abstract void doSomething(Object[] args);
}
除了 你还可以使用 lambda:
Mockito.doAnswer(
invocation -> {
// DO SOMETHING
return null;
}
).when(repoMock).remove(Matchers.<String>anyVararg());
我想存根存储库 class 以测试另一个具有存储库的 class(持有人 class)。 repository接口支持CRUD操作,方法很多,但是我在Holderclass上的单元测试只需要调用其中两个即可。存储库界面:
public interface IRepo {
public void remove(String... sarr);
public void add(String... sarr);
//Lots of other methods I don't need now
}
我想创建一个可以存储实例的存储库模拟,仅为 add
和 remove
定义逻辑,并提供一种检查存储在其中的方法在调用添加和删除之后。
如果我这样做:
IRepo repoMock = mock(IRepo.class);
然后我有一个哑对象,它对每个方法都不做任何事情。没关系,现在我只需要定义添加和删除的行为。
我可以创建一个 Set<String>
并仅存根这两种方法以在集合上工作。然后我将实例化一个具有 IRepo 的持有人,注入部分存根的模拟,并在行使持有人之后,检查集合以验证它包含它应该包含的内容。
我已经设法使用已弃用的方法 stubVoid
:
remove
之类的无效方法
Set<String> mySet = new HashSet<>();
stubVoid(repoMock).toAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
String[] stringsToDelete = (String[]) args[0];
mySet.removeAll(Arrays.asList(stringsToDelete));
return null;
}
}).on().remove(Matchers.<String>anyVararg());
但已被弃用,它并不比为 IRepo 创建部分实现好多少。有没有更好的方法?
注意:Java 请只回答 7 个答案,这应该 运行 在 Android.
假设你有
public class IFace {
public void yourMethod() {
}
}
然后要模拟它你需要
IFace mock = Mockito.mock(IFace.class);
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
//PUT YOUR CODE HERE
return null;
}
}).when(mock).yourMethod();
您可以使用
Mockito.doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
//DO SOMETHING
return null;
}
}).when(...).remove(Matchers.<String>anyVararg());
来自 Javadoc:
Use doAnswer() when you want to stub a void method with generic Answer.
Stubbing voids requires different approach from Mockito.when(Object) because the compiler does not like void methods inside brackets...
Example:
doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Mock mock = invocation.getMock();
return null;
}}).when(mock).someMethod();
请参阅 Mockito 的 javadoc 中的示例
如果您真的不喜欢 return null
在 Answer
的实现中,您可以创建自己的 Answer 实现,委托给 void 方法:
public abstract class DoesSomethingAnswer implements Answer<Void> {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
doSomething(invocation);
return null;
}
protected abstract void doSomething(InvocationOnMock invocation);
}
那么你的测试就少了一行:
Set<String> mySet = new HashSet<>();
Mockito.doAnswer(new DoesSomethingAnswer() {
@Override
protected void doSomething(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
String[] stringsToDelete = (String[]) args[0];
mySet.removeAll(Arrays.asList(stringsToDelete));
}
}).when(repoMock).remove(Matchers.<String> anyVararg());
或者,如果您只需要调用中的参数:
public abstract class DoesSomethingAnswer implements Answer<Void> {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
doSomething(invocation.getArguments());
return null;
}
protected abstract void doSomething(Object[] args);
}
除了
Mockito.doAnswer(
invocation -> {
// DO SOMETHING
return null;
}
).when(repoMock).remove(Matchers.<String>anyVararg());