【问题标题】:How do I handle unmatched parameters in Mockito?如何处理 Mockito 中不匹配的参数?
【发布时间】:2012-10-25 03:44:43
【问题描述】:

我喜欢做以下事情:

.when( 
    myMock.doSomething(
        Matchers.eq( "1" )
    ) 
)
.thenReturn( "1" )
.othwerwise()
.thenThrow( new IllegalArgumentException() );

当然otherwise() 方法不存在,只是为了向您展示我想要完成的工作。

【问题讨论】:

    标签: java junit mockito


    【解决方案1】:

    (轻微的免责声明,我从未亲自做过,只是在 javadoc 中阅读过)...如果您的模拟接口上的所有方法都可以使用相同的默认行为,您可以 set the default answer on your mock in像这样的方式:

    Foo myMock = Mockito.mock(Foo.class,new ThrowsExceptionClass(IllegalArgumentException.class));
    Mockito.when(myMock.doSomething(Matchers.eq("1"))).thenReturn("1");
    

    JavaDoc 链接:Mockito#mockThrowsExceptionClass

    或者,正如Stubbing tutorial 中所讨论的,存根的顺序很重要,最后匹配的获胜,所以你也可以这样做:

    Foo myMock = Mockito.mock(Foo.class);
    Mockito.when(myMock.doSomething(Matchers.any(String.class))).thenThrow(IllegalArgumentException.class);
    Mockito.when(myMock.doSomething(Matchers.eq("1"))).thenReturn("1");
    

    【讨论】:

    • 像魅力一样工作。虽然我觉得他们决定让最后一个匹配者获胜,这很违反直觉。
    • 这正是我更喜欢自定义 Answer 实现方法的原因。我发现它更直接(也更直观)。
    • 一些注意事项。您提供的第一个解决方案很好,但由于默认答案适用于模拟的 每个 方法,因此如果您的测试还调用模拟上的其他方法,则需要小心,此外到你正在测试的那个。我很确定您提供的第二个解决方案将不起作用,因为对doSomething 的第二次调用实际上会调用在第一次调用中设置的行为,因此您将立即获得IllegalArgumentException。 “最后一个存根获胜”行为仅在您使用“doXxx”系列存根方法时才会出现......
    • ...所以在这种特殊情况下,您希望第二次调用是 doReturn("1").when(myMock).doSomething(Matchers.eq("1")); 甚至只是 doReturn("1").when(myMock).doSomething("1");
    • 这对我不起作用。第二个when() 调用抛出异常。
    【解决方案2】:

    您可以创建自己的 Answer 实现,它会注意被调用的参数:

    myMock.doSomething(Mockito.any(String.class)).thenAnswer( myAnswer );
    

    上述答案的实现可以这样做:

    public String answer(InvocationOnMock invocation) {
        if ("1".equals(invocation.getArguments()[0])) {
           return "1";
        }
        else {
           throw new IllegalArgumentException();
        }
    } 
    

    【讨论】:

    • +1。我喜欢这个答案,尽管我已经扩展了一个 Matcher 来匹配一个映射参数。有没有办法针对 invocation.getArguments() 使用我现有的匹配器?
    • 如果正确理解您的问题,我会说将它(或它们)作为构造函数参数传递给您的 Answer 实现,然后简单地针对适当的参数调用 matcher.matches()。跨度>
    【解决方案3】:

    只需使用相反的条件,即考虑您的示例本身。当您需要otherwise 时,您可能想使用not(eq())

     .when( myMock.doSomething(Matchers.eq( "1" )))
         .thenReturn( "1" )
     .when( myMock.doSomething(not(Matchers.eq( "1" ))))
         .thenThrow( new IllegalArgumentException() );
    

    【讨论】:

    • 这种方法的问题是,每次我需要添加新的匹配器时,我都必须添加与该匹配器相反的匹配器。假设我可能有 20 个匹配器,这将需要我添加 20 个相反的条件。真的没有更好的方法吗?
    • @supertonsky 想想简单的 Java。在纯 Java 中,while 没有任何 otherwise。 while 的语法是:满足条件时做某事。同样在这里。我们安排我们的 Java 代码来处理我们的需求。我们需要在这里应用类似的概念。
    • 我不同意。 “while”是一个迭代构造而不是条件构造。 “when”更像是一个条件结构,如“if”。我正在寻找一种像“else”一样工作的解决方案,而不是像另一个“if”那样简单地反转之前的“if”的条件的解决方案,只是为了被抓住。
    • @supertonsky:我并不是说,when 与 Java 中的 while 完全相同。我说过,when 的主体在满足条件时执行,与 while 的主体基于条件相同。我试图解释相似之处只是为了解释这里没有别的。 while 仅用于示例。我不确定分歧在哪里。还有其他构造可以用来实现您正在寻找的东西。
    • 您实际上可以让“一段时间”像“如果”一样工作,反之亦然。如果存在更优雅的解决方案或提供适当的构造,我只是不喜欢通过代码破解来工作。最后,它是关于使用正确的工具来完成正确的工作。
    【解决方案4】:

    使用 java 8 lambda,您可以:

    myMock.doSomething(Mockito.any(String.class)).thenAnswer(invocation -> {    
        Object arg = invocation.getArguments()[0];
        if ("1".equals(arg)) {
            return "1";
        }
    
        throw new IllegalArgumentException("Expected 1 but got " + arg);
    });
    

    【讨论】:

      【解决方案5】:

      @Charlie 接受的答案所描述的方式不再起作用。 当您尝试覆盖某些参数的一般抛出异常行为时,会触发第一个规则并且您遇到异常(正如您所要求的那样)。

      Mockito.when(myMock.doSomething(any()))
          .thenThrow(IllegalArgumentException.class);
      Mockito.when(myMock.doSomething(eq("1"))).thenReturn("1"); //An exception is thrown here
      // because of the call to .doSomething() on the mock object
      

      为了避免调用,可以使用Mockito.doReturn() 方法:

      Mockito.when(myMock.doSomething(any()))
          .thenThrow(IllegalArgumentException.class);
      Mockito.doReturn("1").when(myMock).doSomething(eq("1"));
      

      最初的问题是 doReturn() 根据它的 javadoc 存在的原因之一:

      Here are those rare occasions when doReturn() comes handy:
      <...some lines are skipped...>
      Overriding a previous exception-stubbing:
      
      hen(mock.foo()).thenThrow(new RuntimeException());
      //Impossible: the exception-stubbed foo() method is called so RuntimeException is thrown.
      when(mock.foo()).thenReturn("bar");
      
      //You have to use doReturn() for stubbing:
      doReturn("bar").when(mock).foo();
      

      【讨论】:

        【解决方案6】:

        您也可以使用verify,如下:

        when(myMock.doSomething("1")).thenReturn( "1" );
        assertEquals(myMock.doSomething("1"),"1");
        verify(myMock).doSomething("1")
        

        【讨论】:

          猜你喜欢
          • 2011-11-21
          • 1970-01-01
          • 1970-01-01
          • 2020-06-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多