我的团队最近遇到了类似的问题,我们找到了一个与 jMock 配合得很好的解决方案。也许类似的东西适用于您正在使用的任何模拟库。
假设您的示例中提到的Bar 接口如下所示:
interface Bar {
void useLambda(BazRunnable lambda);
Bam useLambdaForResult(BazCallable<Bam> lambda);
}
interface BazRunnable {
void run(Baz baz);
}
interface BazCallable<T> {
T call(Baz baz);
}
我们创建自定义 jMock Actions 来执行 BazRunnables 和 BazCallables:
class BazRunnableAction implements Action {
private final Baz baz;
BazRunnableAction(Baz baz) {
this.baz = baz;
}
@Override
public Object invoke(Invocation invocation) {
BazRunnable task = (BazRunnable) invocation.getParameter(0);
task.run(baz);
return null;
}
@Override
public void describeTo(Description description) {
// Etc
}
}
class BazCallableAction implements Action {
private final Baz baz;
BazCallableAction(Baz baz) {
this.baz = baz;
}
@Override
public Object invoke(Invocation invocation) {
BazCallable task = (BazCallable) invocation.getParameter(0);
return task.call(baz);
}
@Override
public void describeTo(Description description) {
// Etc
}
}
现在我们可以使用自定义操作来测试与发生在 lambda 中的模拟依赖项的交互。要从您的示例中测试方法 void method(int foo),我们会这样做:
Mockery context = new Mockery();
int foo = 1234;
Bar bar = context.mock(Bar.class);
Baz baz = context.mock(Baz.class);
context.checking(new Expectations() {{
oneOf(bar).useLambda(with(any(BazRunnable.class)));
will(new BazRunnableAction(baz));
oneOf(baz).setFoo(foo);
}});
UnitBeingTested unit = new UnitBeingTested(bar);
unit.method(foo);
context.assertIsSatisfied();
我们可以通过向 Expectations 类添加便捷方法来节省一些样板:
class BazExpectations extends Expectations {
protected BazRunnable withBazRunnable(Baz baz) {
addParameterMatcher(any(BazRunnable.class));
currentBuilder().setAction(new BazRunnableAction(baz));
return null;
}
protected <T> BazCallable<T> withBazCallable(Baz baz) {
addParameterMatcher(any(BazCallable.class));
currentBuilder().setAction(new BazCallableAction(baz));
return null;
}
}
这使得测试期望更加清晰:
context.checking(new BazExpectations() {{
oneOf(bar).useLambda(withBazRunnable(baz));
oneOf(baz).setFoo(foo);
}});