【问题标题】:Mockito. How to return a boolean value based on mocked object as parameter?莫基托。如何基于模拟对象作为参数返回布尔值?
【发布时间】:2017-09-06 20:21:13
【问题描述】:
Class A
{
public B makeB(int q)
    {
        return new B(q);
    }
    public boolean evaluate(int q)
    {
        B b = makeB(q);
        boolean result = b.get();
        return result;
    }
}

Class B
{
    int e;
    public B(int w)
    {
        e=w;
    }
    public boolean get()
    {
        //return boolean based on object B
    } 
}

Class ATest
{
    A a = spy(A.class);
    B b1 = mock(B.class);
    B b2 = mock(B.class);

    doReturn(b1).when(a).makeB(5);
    doReturn(b2).when(a).makeB(10);

    when(b1.get()).thenReturn(true);
    when(b2.get()).thenReturn(false);

    a.evaluate();
}

=========================

这里我想在对象 B 包含值 5 时从方法评估返回 true,如果它包含值 10,则返回 false。

B 类来自外部库。

单元测试和模拟的新手。

【问题讨论】:

  • 使用 if else 语句

标签: java spring-boot junit mockito


【解决方案1】:

其他答案在技术上是正确的,但首先要理解:您应该努力不使用这样的模拟框架。

请记住:模拟框架的目的仅是为了使测试成为可能/更容易。您的模拟规范应该尽可能简单。含义:与其考虑让一个模拟在不同的参数上给出不同的结果 - 更好的解决方案是进行 distinct 测试和模拟规范,例如:

@Test 
public void testFooWithA() {
  when(someMock.foo(eq(whateverA)).thenReturn(bar);
  ...

@Test 
public void testFooWithB() {
  when(someMock.foo(eq(whateverB)).thenReturn(somethingElse);
  ...

在某些情况下,您编写了更复杂的代码以使您的模拟“更智能”。但大多数时候,当我不得不这样做时——我退后一步,简化我的测试设计。换句话说:当您的测试变得“太复杂”时 - 最常见的原因是测试中的太复杂类/方法。

【讨论】:

    【解决方案2】:

    您可以使用 Mockito 的 Answer 接口来实现。它使您可以访问调用的实际参数,并且您可以据此区分返回值。

    给定接口

    interface Measurer {
        int measureLength(String s);
    }
    

    示例用法如下所示

    Measurer measurer = Mockito.mock(Measurer.class);
    Mockito.when(measurer.measureLength(Mockito.any(String.class)))
            .then((Answer<Integer>) invocationOnMock -> {
                String arg = invocationOnMock.getArgumentAt(0, String.class);
                if (arg.length() < 10) {
                    // mock Measurer can measure only short strings
                    return arg.length();
                }
                else {
                    return 42;
                }
            });
    
    System.out.println(measurer.measureLength("abc")); // 3
    System.out.println(measurer.measureLength("abcdefghijklmn")); // 42
    

    【讨论】:

      【解决方案3】:

      在这种情况下,你可以这样做:

      doAnswer(new Answer<Boolean>() {
           public Boolean answer(InvocationOnMock invocation) {
               return invocation.getArgument(0) == 5;
           }}).when(a.evaluate());
      

      如果您询问如何在 A#evaluate() 的实际实现中获取 'b' 的实际值,那么您可以为两个将局部变量设置为 B 实例的模拟添加 makeB 的答案那是返回的,所以你知道它最后一次被调用时返回了什么,然后你可以根据那个局部变量的值得到你的 evaluate() 答案。

      即:

      B lastB = null;
      Answer<B> lastBAnswer = new Answer<B>() {
        public B answer(InvocationOnMock invocation) {
          if (invocation.<Integer>getArgument(0) == 5) {
            lastB = b1;
          } else {
            lastB = b2;
          }
          return lastB;
        }
      };
      doAnswer(lastBAnswer).when(a).makeB(any());
      doAnswer(new Answer<Boolean>() {
        public Boolean answer(InvocationOnMock invocation) {
          return lastB.get();
        }
      }).when(a).evaluate();
      

      我不推荐这条路径,因为它依赖于时间凝聚力,但对于测试它是可以接受的。

      注意:通过 StackOverflow 回答而不进行语法检查。可能略有偏差,但这是粗略的想法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-04
        • 1970-01-01
        • 2023-01-26
        • 2017-05-11
        • 2016-07-05
        • 1970-01-01
        相关资源
        最近更新 更多