6

これらの行に沿ったテストがあります:

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

//do my thing

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

これは問題なく動作しますが、最初の呼び出しが 2 番目の呼び出しとは異なるボディを渡すことを確認したいと思います。本体がかなり大きく、厳密な例で正確なマッチングを行いたくありません。私はこれを試しました:

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: ...

このようなことが可能であるように思えますが、理解できないようです。洞察?

4

1 に答える 1

7

そもそもこれは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")

これを回避する方法の 1 つは、引数の連続する値をチェックするマッチャーを定義することです。

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 ...
于 2013-03-02T15:08:28.640 に答える