Site cover image

fhhm’s blog

Mockitoで引数の検証がうまくできなかった

概要

Mockitoで引数の検証をするときに、引数の取り方や検証の方法によっては意図通りには動かないことを知ったので、まとめておく。

検証したかったこと

saveが呼ばれるとき、その引数であるUsername, emailが期待通りか検証したかった。

case class User(name: String, email: String)

class Test {
  def test(user: User): Unit = ???
}

検証

正しく検証できていないケース

User(”John”, “test@example.com”)でモックを呼び、User(”Alice”, any[String])と比較してみると検証成功扱いとなってしまう。

Image in a image block
📝 コード
import $ivy.`org.mockito:mockito-core:5.11.0`
import org.mockito.Mockito.*
import org.mockito.ArgumentMatchers.*

object UserServiceTest {

  case class User(name: String, email: String)

  class Test {
    def test(user: User): Unit = ???
  }

  def run(): Unit = {
    val _mock = mock(classOf[Test])
    val user = User("John", "test@example.com")
    _mock.test(user)

    try
      // "John" != "Alice"なので失敗してほしい
      verify(_mock).test(User("Alice", any[String]))
      println("PASS")
    catch
      case e: Throwable =>
        println(s"FAIL:, ${e.getMessage}")
  }
}

UserServiceTest.run()

原因

verifyが検証するのは対象となるメソッドの引数そのもの(例で言うとUser)であり、User.emailに登録されたmatcherは検証されなかったっぽい。また、Mockitoはmatcherのネストをサポートしておらず、検証ロジックが正しく動かないようになっていたようだった。

そもそも、ある引数で呼ばれていることを検証するには、eq_やargThatを使うべきなので、Mockitoの使い方がいろいろと間違っていた 😇

どうすれば正しく検証できるか

例のようなケースではargThatを使うのが適切らしい。

Image in a image block
📝 コード
import $ivy.`org.mockito:mockito-core:5.11.0`
import org.mockito.Mockito.*
import org.mockito.ArgumentMatchers.*

object UserServiceTest {

  case class User(name: String, email: String)

  class Test {
    def test(user: User): Unit = ???
  }

  def run(): Unit = {
    val _mock = mock(classOf[Test])
    val user = User("John", "test@example.com")
    _mock.test(user)

    try
      // 失敗ケース
      verify(_mock).test(argThat(u => u.name == "Alice"))
      println("name is Alice")
    catch
      case e: Throwable =>
        println(s"FAIL:, ${e.getMessage}")

    try
      // 成功ケース
      verify(_mock).test(argThat(u => u.name == "John"))
      println("name is John")
    catch
      case e: Throwable =>
        println(s"FAIL:, ${e.getMessage}")
  }
}

UserServiceTest.run()