2014-12-12 7 views
11

Dato quanto segue, come faccio a simulare processMessage() usando Spock, in modo che possa verificare che processBulkMessage() chiami processMessage() n volte, dove n è il numero di messaggi all'interno di BulkMessage?Grails/Spock: come prendere in giro un singolo metodo all'interno della classe in cui il metodo viene chiamato all'interno della classe stessa?

class BulkMessage { 
    List messages 
} 

class MyService { 

    def processBulkMessage(BulkMessage msg) { 
     msg.messages.each {subMsg-> 
      processMessage(subMsg) 
     } 
    } 

    def processMessage(Message message) { 

    } 
} 

risposta

5

È possibile utilizzare spies e parziali schernisce (richiede Spock 0.7 o più recente).

Dopo la creazione di una spia, è possibile ascoltare la conversazione tra il chiamante e l'oggetto reale alla base della spia:

def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"]) 
subscriber.receive(_) >> "ok" 

A volte, è auspicabile sia per eseguire del codice e delegare al metodo vero e proprio:

subscriber.receive(_) >> { String message -> callRealMethod(); message.size() > 3 ? "ok" : "fail" } 
1

Non usa Spock built-in derisione API (non so come per deridere parzialmente un oggetto), ma questo dovrebbe fare il trucco:

class FooSpec extends Specification { 

    void "Test message processing"() { 
     given: "A Bulk Message" 
     BulkMessage bulk = new BulkMessage(messages: ['a', 'b', 'c']) 

     when: "Service is called" 
     def processMessageCount = 0 
     MyService.metaClass.processMessage { message -> processMessageCount++ } 
     def service = new MyService() 
     service.processBulkMessage(bulk) 

     then: "Each message is processed separately" 
     processMessageCount == bulk.messages.size() 
    } 
} 
2

I A mio avviso questa non è una soluzione ben progettata. Test e design camminano mano nella mano - Io consiglio di parlare con lo this per indagare meglio. Se è necessario verificare se è stato richiamato un altro metodo su un oggetto sottoposto a test, sembra che debba essere spostato su un altro oggetto con responsabilità diverse.

Ecco come lo farei. So come funziona la visibilità in groovy quindi attenzione ai commenti.

@Grab('org.spockframework:spock-core:0.7-groovy-2.0') 
@Grab('cglib:cglib-nodep:3.1') 

import spock.lang.* 

class MessageServiceSpec extends Specification { 

    def 'test'() { 
     given: 
     def service = new MessageService() 
     def sender = GroovyMock(MessageSender) 

     and: 
     service.sender = sender 

     when: 
     service.sendMessages(['1','2','3']) 

     then: 
     3 * sender.sendMessage(_) 
    } 
} 
class MessageSender { //package access - low level 
    def sendMessage(String message) { 
     //whatever 
    } 
} 

class MessageService { 

    MessageSender sender //package access - low level 

    def sendMessages(Iterable<String> messages) { 
     messages.each { m -> sender.sendMessage(m) } 
    } 
} 
+0

Grazie molto - Ho appena rivisitato questo e ho imparato molto su disegno da quando ho fatto la domanda. Ora sono d'accordo con te sul fatto che il design migliore sarebbe un bulkMessageProcessingService e un individualMessageProcessingService. Il test è quindi banale con una finta. – John

1

Per Java Primavera gente test in Spock:

constructorArgs è la strada da percorrere, ma utilizzare l'iniezione costruttore. Spy() non ti consente di impostare direttamente i campi autowired.

// **Java Spring** 
class A { 
    private ARepository aRepository; 

    @Autowire 
    public A(aRepository aRepository){ 
     this.aRepository = aRepository; 
    } 

    public String getOne(String id) { 
     tryStubMe(id) // STUBBED. WILL RETURN "XXX" 
     ... 
    } 

    public String tryStubMe(String id) { 
     return aRepository.findOne(id) 
    } 

    public void tryStubVoid(String id) { 
     aRepository.findOne(id) 
    } 
} 

// **Groovy Spock** 
class ATest extends Specification { 

    def 'lets stub that sucker' { 
     setup: 
      ARepository aRepository = Mock() 
      A a = Spy(A, constructorArgs: [aRepository]) 
     when: 
      a.getOne() 
     then: 
      // Stub tryStubMe() on a spy 
      // Make it return "XXX" 
      // Verify it was called once 
      1 * a.tryStubMe("1") >> "XXX" 
    } 
}  

Spock - spegnendo metodo vuoto su Spy oggetto

// **Groovy Spock** 
class ATest extends Specification { 

    def 'lets stub that sucker' { 
     setup: 
      ARepository aRepository = Mock() 
      A a = Spy(A, constructorArgs: [aRepository]) { 
       1 * tryStubVoid(_) >> {} 
      } 
     when: 
      ... 
     then: 
      ... 
    } 
}