【问题标题】:Same answer 10 times for a mock invocation, and then another answer after that, with Mockito模拟调用 10 次相同的答案,然后使用 Mockito 进行另一个答案
【发布时间】:2016-09-15 20:23:48
【问题描述】:

我希望我的模拟在被调用 10 次时返回 method1(),并在之后调用时返回 method2()。下面的代码适用于匿名内部类。在 Java 8 中是否有一种优雅的方式来做到这一点?

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();
        }
    }
);

【问题讨论】:

    标签: java java-8 mockito


    【解决方案1】:

    由于像计数器这样的可变状态和 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);
    }
    

    相当于使用

    Mockito.when(mock.doSomething()).then(x -> method1()).then(x -> method1())
           .then(x -> method1()).then(x -> method1()).then(x -> method1()).then(x -> method1())
           .then(x -> method1()).then(x -> method1()).then(x -> method1()).then(x -> method1())
           .then(x -> method2());
    

    用作

    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()));
    

    【讨论】:

    • 不错!感谢您的解决方案。
    【解决方案2】:

    Answer 是一个单方法接口,因此您可以简化匿名内部类样板。我认为没有任何内置的替代方法可以简化您的逻辑,除非您将其提取到您编写的方法中(例如 firstNTimes(invocation -&gt; method1(), 10, invocation -&gt; method2()))。

    when(mock.doSomething()).thenAnswer(invocationOnMock -> {
      if (count < 10) {
        count++;
        return method1();
      }
      return method2();
    });
    

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

    【讨论】:

    • 你避开了count 的要求声明。因为如果 count 是一个局部变量,这不起作用,它必须在其他地方声明......
    • 是的,这是行不通的,因为我们正在改变声明为 final 的计数,它会引发编译错误。
    • @Holger 是的,虽然您可以轻松地对可变对象(如 AtomicInteger)创建一个 final 引用来跟踪计数,但我同意这使它看起来没有人们希望的那么简洁为。
    • 当创建一个对象来保存可变变量并将count++替换为getAndIncrement()(在使用AtomicInteger的情况下)时,您将失去lambda表达式相对于原始匿名内部的任何优势关于(源代码)简单性和(运行时)性能的类方法......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多