概要
Mockitoで引数の検証をするときに、引数の取り方や検証の方法によっては意図通りには動かないことを知ったので、まとめておく。
検証したかったこと
saveが呼ばれるとき、その引数であるUserのname, emailが期待通りか検証したかった。
case class User(name: String, email: String)
class Test {
def test(user: User): Unit = ???
}検証
正しく検証できていないケース
User(”John”, “test@example.com”)でモックを呼び、User(”Alice”, any[String])と比較してみると検証成功扱いとなってしまう。
📝 コード
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を使うのが適切らしい。
📝 コード
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()

