Help us understand the problem. What is going on with this article?

名前渡し必要なメソッドのモックが地獄

More than 5 years have passed since last update.

名前渡しするメソッドをもつやつのモックを定義しようとしたら地獄だった。

モック化する奴
trait Foo {
  def nyan(s: => String, repeat: Long)
}

このモックを verify しようとするとこんな風に書かないといけなくなる:

地獄.scala
import org.mockito.ArgumentMatcher
import org.scalatest.FunSuite
import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito._
import org.mockito.Matchers._
import org.mockito.Matchers

class MockingWithByName extends FunSuite with MockitoSugar {

  test("mocking with a by-name param and AnyRef param") {
    val foo = mock[Foo]
    val sound = "nyaaaaan"
    val repeat = 1111L

    //

    foo.nyan(sound, repeat)

    //

    // ダメ
    //verify(foo, times(1)).nyan(sound, repeat)
    // => Argument(s) are different! Wanted:
    // 問答無用で Function0 にラップされて nyan に渡されるので、
    // 実際に呼ばれた時と、verify 時で、違う Function0 のインスタンスになってしまうため。

    // じゃあこうすると ...
    classOf[Foo].getMethod("nyan", classOf[Function0[_]], classOf[Long])
      .invoke(
        verify(foo),
        argThat(new ArgumentMatcher[Function0[_]] {
          override def matches(argument: Any): Boolean =
            argument.asInstanceOf[Function0[_]].apply() == sound
        }),
        //Matchers.eq(repeat) // ダメ
        // ↑ invoke の第二引数以降は AnyRef 期待されてるんだけど、
        // nyan 自体は scala.Long (AnyRef でないクラス)を期待してるので無理
        Matchers.eq(new java.lang.Long(repeat)) // こうする必要がある
      )

    // 完
  }
}

// モックにするやつ
trait Foo {
  def nyan(s: => String, repeat: Long)
}

スタブ作ったり、when で返り値の定義をする時にも同様の書き方が必要になります。

つらすぎる。今回使った Mockito 以外にも Java 向けのモックフレームワークを薄くラップしてる系のものは同じ悩みを抱えるはずなんですが、もっとスマートに書かける方法/モックフレームワークないの。

k_ui
ねこほしい
http://k-ui.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away