Java 对于模拟调用,同样的回答10次,然后用Mockito再回答一次

Java 对于模拟调用,同样的回答10次,然后用Mockito再回答一次,java,java-8,mockito,Java,Java 8,Mockito,我希望我的mock在调用10次时返回method1,然后在调用之后返回method2。下面的代码使用匿名内部类。在Java8中有没有一种优雅的方法可以做到这一点 when(mock.doSomething()).thenAnswer( new Answer<Account>() { private int count = 0; @Override public Account answer(InvocationOnMock i

我希望我的mock在调用10次时返回method1,然后在调用之后返回method2。下面的代码使用匿名内部类。在Java8中有没有一种优雅的方法可以做到这一点

when(mock.doSomething()).thenAnswer(
    new Answer<Account>() {
        private int count = 0;

        @Override
        public Account answer(InvocationOnMock invocationOnMock) throws Throwable {
            if (count < 10) {
                count++;
                return method1();
            }
            return method2();
        }
    }
);
答案是一个单一的方法接口,所以您可以简化匿名内部类样板。我不认为有任何内置的替代方案可以简化逻辑,除非您将其提取到您编写的方法中,如firstNTimesinvocation->method1,10,invocation->method2


我没有机会测试这个;如果Throwable或Answer泛型给您带来任何麻烦,请发表评论,我再看一看。

由于计数器之类的可变状态和Java 8的lambda表达式不能很好地协同工作,因此没有直接的、针对Java 8的计数器解决方案。每一次试图找到一个聪明的解决办法都会比下面的无计数器解决方案更糟糕

public static <T> OngoingStubbing<T> switchAfter(
              OngoingStubbing<T> stub, int calls, Supplier<T> first, Supplier<T> then) {
    Answer<T> a1=x -> first.get(), a2=x -> then.get();
    while(calls-->0) stub=stub.then(a1);
    return stub.then(a2);
}
可用作

switchAfter(Mockito.when(mock.doSomething()), 10, () -> method1(), () -> method2());
再想一想,有一种解决方案在代码方面并不简单,但如果第一次调用的数量较大,则更可取:

public static <T> Answer<T> switchAfter(int calls, Supplier<T> first, Supplier<T> then) {
    Iterator<T> it=Stream.concat(
            IntStream.range(0, calls).mapToObj(i -> first.get()),
            Stream.generate(then))
        .iterator();
    return x -> it.next();
}

你逃避了要求的计数声明。因为如果count是一个局部变量,它就不起作用了,所以必须在其他地方声明它……是的,这不起作用,因为我们正在对声明为final的count进行变异,这会引发编译错误。@Holger True,尽管您可以很容易地对像AtomicInteger这样的可变对象进行最终引用以跟踪计数,我同意这会让它看起来没有人们希望的那么简洁。当创建一个对象来保存可变变量并在使用AtomicInteger的情况下用getAndIncrement替换count++时,lambda表达式在这两方面都失去了原始匿名内部类方法的优势,源代码简单性和运行时性能…很好!谢谢你的解决方案。
switchAfter(Mockito.when(mock.doSomething()), 10, () -> method1(), () -> method2());
public static <T> Answer<T> switchAfter(int calls, Supplier<T> first, Supplier<T> then) {
    Iterator<T> it=Stream.concat(
            IntStream.range(0, calls).mapToObj(i -> first.get()),
            Stream.generate(then))
        .iterator();
    return x -> it.next();
}
Mockito.when(mock.doSomething()).then(switchAfter(10, () -> method1(), () -> method2()));