2010-01-28 13 views
19

Sto cercando di prendere in giro una chiamata di metodo che accetta una chiamata per nome argomento:Come schernire un metodo con argomenti funzionali in 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) 
     } 
    } 
} 

Sono principalmente interessato a Mockito dato che è quello che sto usando, ma sarei interessato a vedere se uno dei principali quadri di simulazione è capace di questo tipo di test. Il test fallisce in fase di esecuzione sulla linea verify, con un errore come

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) 

Se ho la comprensione della situazione in modo corretto, il compilatore è implicitamente avvolgendo t in una funzione che restituisce nullaria t. Il framework fittizio sta quindi confrontando quella funzione con quella prodotta nel codice di test, che è equivalente ma non è equals().

Il mio caso è una versione relativamente semplice del problema, ma credo che questo sarebbe un problema con qualsiasi funzione di ordine superiore.

+3

Non capisco come questo si riferisce al iPad di Apple. –

+0

Puoi descrivere come non funziona in modo più chiaro o includere un codice di prova. È difficile sapere cosa sta succedendo qui solo con la verifica mostrata. –

+0

Ho reso l'esempio eseguibile come scritto e incluso l'output effettivo associato all'errore. –

risposta

9

Questo sembra brutto, ma si spera che può aiutare a trovare una buona soluzione:

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

Questo problema sembra essere specifico di invocazioni per nome, perché in normali funzioni di ordine superiore è possibile abbinare contro il FunctionX esplicito oggetto:

verificare (collaboratore) .somethingElse (qualsiasi (Function2 [String, Thing]))

nel caso per nome l'involucro dell'argomento in un Function0 è fatto in modo implicito, e Alexey di risposta mostra come invocare il mock con un parametro esplicito.

Si potrebbe scrivere qualcosa di simile al tuo verificare che si applicherebbe argomenti catturati da Mockito.

Mockito internamente record invocazione e le loro argomentazioni con ad es .: http://code.google.com/p/mockito/source/browse/trunk/src/org/mockito/internal/matchers/CapturingMatcher.java

+0

Questo non funziona, poiché la funzione prevede un singolo parametro call-by-name di tipo Cosa (: => Thing), che non è dello stesso tipo di Function2 [String, Thing] (: String => Thing). È necessario un tipo di tipo più elevato per digitare i parametri call-by-name per poter utilizzare i matcher. – Woodz

0

È possibile provare specs2. In specs2, noi "dirottare" la classe Mockito Invocation per tenere conto di parametri Byname:

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

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

Ciao Eric, ho copiato lo snippet in una nuova app per Play 2.2.3 e non funziona ancora. Sto usando una semplice classe di test 'mutable.Specification with Mockito',' specs2_2.10-2.1.1' e 'mockito-core-1.9.5' (dichiarata esplicitamente in' build.sbt', assicurandomi che specs2 sia dichiarata prima di mockito) Qualche indizio sul perché fallisce ancora? – jmelanson

+0

È necessario assicurarsi che il barattolo specs2 arrivi prima del barattolo di Mockito sul classpath. – Eric

+0

Qualche suggerimento su come farlo con SBT? Ho già provato a cambiare l'ordine di dichiarazione. – jmelanson

Problemi correlati