【问题标题】:How do I verify invokations with specific string matchers in Specs2 with Mockito如何使用 Mockito 验证 Specs2 中特定字符串匹配器的调用
【发布时间】:2013-03-02 10:01:33
【问题描述】:

我有一个这样的测试:

httpClient.post(anyString, anyString) returns (first, second)

//do my thing

there were two(httpClient).post(anyString, anyString)

这工作正常,但我想验证第一个调用传递的主体与第二个调用不同。身体相当大,我不想在严格的例子上进行精确匹配。我试过这个:

there was one(httpClientMock).postMessage(anyString, argThat(contain("FOO"))
there was one(httpClientMock).postMessage(anyString, argThat(contain("FOO"))

这让 Mockito 抱怨:

InvalidUseOfMatchersException: 
 [error] Invalid use of argument matchers!
 [error] 2 matchers expected, 3 recorded:

我也试过了:

  there was one(httpClientMock).postMessage(argThat(contain("foo")), argThat(contain("FOO")))
  there was one(httpClientMock).postMessage(argThat(contain("foo")), argThat(contain("FOO")))

导致:

Wanted 1 time:
 [error] -> ...
 [error] But was 2 times. Undesired invocation: ...

在我看来,这样的事情应该是可能的,但我似乎无法弄清楚。见解?

【问题讨论】:

    标签: scala mockito specs2


    【解决方案1】:

    我认为这首先是 Mockito 的一个问题。当您使用带有 specs2 的 Mockito 并且有疑问时,请始终直接使用 Mockito API:

    // simplified httpClient with only one parameter
    val httpClient = mock[HttpClient]
    httpClient.post(anyString) returns ""
    
    httpClient.post("s1")
    httpClient.post("s2")
    
    // forget specs2
    // there was two(httpClient).post(anyString)
    
    org.mockito.Mockito.verify(httpClient, org.mockito.Mockito.times(1)).post("s1")
    
    // I guess that you don't want this to pass but it does
    org.mockito.Mockito.verify(httpClient, org.mockito.Mockito.times(1)).post("s1")
    

    一种可能的解决方法是定义一个匹配器来检查参数的连续值:

    there was two(httpClient).post(consecutiveValues(===("s1"), ===("s2")))
    

    consecutiveValues 匹配器是这样定义的:

    import matcher._
    import MatcherImplicits._
    
    // return a matcher that will check a value against a different
    // `expected` matcher each time it is invoked
    def consecutiveValues[T](expected: Matcher[T]*): Matcher[T] = {
      // count the number of tested values
      var i = -1
    
      // store the results
      var results: Seq[(Int, MatchResult[T])] = Seq()
    
      def result(t: T) = {
        i += 1
        // with Mockito values are tested twice
        // the first time we return the result (but also store it)
        // the second time we return the computed result
        if (i < expected.size) {
          val mr = expected(i).apply(Expectable(t))
          results = results :+ (i, mr)
          mr
         } else results(i - expected.size)._2
      }
    
      // return a function that is translated to a specs2 matcher
      // thanks to implicits
      // display the failing messages if there are any
      (t: T) => (result(t).isSuccess,
                 results.filterNot(_._2.isSuccess).map { case (n, mr) => 
                   s"value $n is incorrect: ${mr.message}" }.mkString(", "))
    }
    

    您可以测试上面的代码。失败消息不是最好的,但可以解决问题。在这种情况下:

    httpClient.post("s1") 
    httpClient.post("s2")
    
    there was two(httpClient).post(consecutiveValues(===("s1"), ===("s3")))
    

    你会看到:

    [error] x test
    [error]  The mock was not called as expected: 
    [error]  httpClient.post(
    [error]      value 1 is incorrect: 's2' is not equal to 's3'
    [error]  );
    [error]  Wanted 2 times:
    [error]  -> at ... 
    [error]  But was 1 time:
    [error]  -> at ...
    

    【讨论】:

    • 我从不怀疑这是可能的。和往常一样,我很高兴我问了:)
    • 这引出了一个问题,Scala 是否有替代 Mockito 的方法?
    • 还有一种选择:scalamock.org。但也许你可以在 Mockito 邮件列表上询问这是否是一个错误。在这种情况下,他们可能会修复它。
    • 另外,如果你想测试比电话号码更多的值,你可以写:there was atLeastOne(httpClient).post(anyString) andThen atLeastOne(httpClient).post(===("s2"))
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多