2010-01-28 17 views
19

Estoy tratando de burlarse de una llamada a un método que toma un argumento de llamada por nombre:Cómo simular un método con argumentos funcionales en 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) 
     } 
    } 
} 

estoy interesado principalmente en Mockito ya que eso es lo que estoy usando, pero me gustaría que se pusiera ver si alguno de los principales frameworks simulados es capaz de realizar este tipo de pruebas. La prueba falla en tiempo de ejecución en la línea verify, con un error como

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) 

Si estoy entendiendo correctamente la situación, el compilador está implícitamente terminando t en una función que devuelve nularia t. El marco simulado está comparando esa función con la que se produce en el código de prueba, que es equivalente pero no equals().

Mi caso es una versión relativamente simple del problema, pero creo que esto sería un problema con cualquier función de orden superior.

+3

No entiendo cómo esto se relaciona con el iPad de Apple. –

+0

¿Podría describir cómo no funciona más claramente o incluir algún código de prueba? Es difícil saber qué está pasando aquí solo con la verificación que se muestra. –

+0

Hice el ejemplo ejecutable tal como está escrito e incluí la salida real asociada con la falla. –

Respuesta

9

Esto se ve feo, pero es de esperar que puede ayudar a encontrar una buena solución:

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() 
     }) 
} 
1

Este problema parece ser específica a las invocaciones por-nombre porque en las funciones regulares de orden superior puede hacer coincidir en contra de la FunctionX explícita objeto:

verificar (colaborador) .somethingElse (cualquier (Función2 [String, cosa]))

en el caso por el nombre de la envoltura del argumento en un Function0 se realiza de forma implícita, y Alexey de respuesta muestra cómo invocar el simulacro con un parámetro explícito.

Puede escribir algo similar a su verificación, que aplicaría argumentos capturados por mockito.

Mockito internamente registros de invocación y sus argumentos con ej .: http://code.google.com/p/mockito/source/browse/trunk/src/org/mockito/internal/matchers/CapturingMatcher.java

+0

Esto no funciona, ya que la función espera un único parámetro de llamada por nombre de tipo Thing (: => Thing), que no es del mismo tipo que Function2 [String, Thing] (: String => Thing). Es necesario que haya un tipo de mayor nivel para escribir los parámetros de llamada por nombre con el fin de utilizar los mezcladores. – Woodz

0

Puede probar specs2. En specs2, que "secuestrar" la clase Mockito Invocation para tener en cuenta los parámetros Byname:

trait ByName { def call(i: =>Int) = i } 
val byname = mock[ByName] 

byname.call(10) 
there was one(byname).call(10) 
+0

Hola Eric, copié ese fragmento en una nueva especificación de la aplicación Play 2.2.3 y todavía falla. Estoy usando una clase de prueba 'mutable.Specification with Mockito' simple,' specs2_2.10-2.1.1' y 'mockito-core-1.9.5' (declarada explícitamente en' build.sbt', asegurándome de que se especifique specs2 antes de mockito) ¿Alguna pista de por qué todavía falla? – jmelanson

+0

Debe asegurarse de que el jar specs2 venga antes del jar Mockito en el classpath. – Eric

+0

¿Alguna pista de cómo hacerlo con SBT? Ya traté de cambiar el orden de las declaraciones. – jmelanson

Cuestiones relacionadas