Как высмеять метод с функциональными аргументами в Scala?
Я пытаюсь высмеять вызов метода, который принимает аргумент по вызову:
import org.scalatest.WordSpec
import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito._
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
trait Collaborator {
def doSomething(t: => Thing)
}
trait Thing
@RunWith(classOf[JUnitRunner])
class Test extends WordSpec with MockitoSugar {
"The subject under test" should {
"call the collaborator" in {
// setup
val m = mock[Collaborator]
val t = mock[Thing]
// test code: this would actually be invoked by the SUT
m.doSomething(t)
// verify the call
verify(m).doSomething(t)
}
}
}
В первую очередь меня интересует Mockito с тех пор, что я использую, но мне было бы интересно узнать, способен ли какой-либо из основных макетных фреймворков на такой тип тестирования. Тест не выполняется во время выполнения в строке verify
с ошибкой, например
Argument(s) are different! Wanted:
collaborator.doSomething(
($anonfun$apply$3) <function>
);
-> at Test$$anonfun$1$$anonfun$apply$1.apply(Test.scala:27)
Actual invocation has different arguments:
collaborator.doSomething(
($anonfun$apply$2) <function>
);
-> at Test$$anonfun$1$$anonfun$apply$1.apply(Test.scala:24)
Если я правильно понимаю ситуацию, компилятор неявно обертывает t
в нулевой функции, которая возвращает t
. Макетная структура затем сравнивает эту функцию с той, что была получена в тестовом коде, что эквивалентно, но не equals()
.
Мой случай - относительно простая версия проблемы, но я думаю, что это будет проблемой с любой функцией более высокого порядка.
Ответы
Ответ 1
Это выглядит некрасиво, но, надеюсь, это поможет вам найти хорошее решение:
import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito._
trait Collaborator {
def doSomething(t: => Thing)
}
trait Thing
new MockitoSugar {
// setup
val m = mock[Collaborator]
val t = mock[Thing]
m.doSomething(t)
classOf[Collaborator].getMethod("doSomething", classOf[Function0[_]]).invoke(
verify(m),
new Function0[Thing] {
def apply() = null
override def equals(o: Any): Boolean = t == o.asInstanceOf[Function0[Thing]].apply()
})
}
Ответ 2
Эта проблема, по-видимому, специфична для вызовов по имени, потому что в регулярных функциях более высокого порядка вы можете сопоставлять явный объект FunctionX:
проверить (соавтор).somethingElse(any (Function2 [String, Thing]))
в поименном случае обертка аргумента в Function0 выполняется неявно, а ответ Alexey показывает, как вызывать макет с явным параметром.
Вы можете написать что-то похожее на свою собственную проверку, которая будет применять аргументы, захваченные mockito.
Mockito внутренне записывает вызов и их аргументы, например:
http://code.google.com/p/mockito/source/browse/trunk/src/org/mockito/internal/matchers/CapturingMatcher.java
Ответ 3
Вы можете попробовать specs2. В specs2 мы "захватываем" класс Mockito Invocation
для учета параметров имени:
trait ByName { def call(i: =>Int) = i }
val byname = mock[ByName]
byname.call(10)
there was one(byname).call(10)