2014-04-28 10 views
5

Sto usando Grails 2.3.7. Ho un semplice oggetto di dominio Persona:Mocking static get gorm method in Grails unit test

class Person { 
    String name 
    static constraints = {} 
} 

e il controller:

@Transactional(readOnly = true) 
class PersonController { 
    def index() { 
    render view:'index', model:[personList:Person.list()] 
    } 

    def show(Long id) { 
    def person = Person.get(id) 
    render view:'show', model:[person:person] 
    } 
    ... 
} 

Sto cercando di scrivere uno unit test per il controller di esercitare il metodo show, ma non sono sicuro di come mi è necessario configurare il test per garantire che la chiamata statica Person.get(id) restituisca un valore.

Ecco il mio (non) tentativo:

import grails.test.mixin.* 
import spock.lang.* 

@TestFor(PersonController) 
@Mock(Person) 
class PersonControllerSpec extends Specification { 

    void "Test show action loads Person by id and includes that person in the model rendered"() { 
    setup: 
     def personMockControl = mockFor(Person) 
     personMockControl.demand.static.get() { int id -> new Person(name:"John") } 

    when:"A valid person id passed to the show action" 
     controller.show(123) 

    then:"A model is populated containing the person domain instance loaded by that unique id" 
     model.person != null 
     model.person.name == "John" 
    } 
} 

Questo test non è riuscito perché la condizione model.person != null fallisce. Come posso riscrivere questo test per farlo passare?

EDIT

In questo caso posso eludere la chiamata al metodo statico rielaborazione del test di questa convenzione:

void "Test show action loads Person by id and includes that person in the model rendered"() { 
    setup: 
    def p = new Person(name:"John").save() 

    when:"A valid person id passed to the show action" 
    controller.show(p.id) 

    then:"A model is populated containing the person domain instance loaded by that unique id" 
    model.person != null 
    model.person.id == p.id 
    model.person.name == "John" 
} 

Tuttavia, mi piacerebbe davvero capire come deridere correttamente la Person ' s metodo statico get.

EDIT 2

Ecco un'altra situazione per dimostrare il mio bisogno percepito per deridere il metodo get. Dato questo codice del filtro:

def fitlers = { 
    buyFilter(controller:"paypal", action:"buy") { 
    after = { 
     new VendorPayment(payment:request.payment, vendor: Vendor.get(params.buyerId)).save() 
    } 
    } 
} 

sto cercando di provare che questo filtro funziona come previsto utilizzando il seguente test:

void "test the buyFilter to ensure it creates a VendorPayment when the PayPal plugin controller is called"() { 
    setup: 
    // how do I mock the Vendor.get(params.buyerId) 
    // to return a vendor in order to save a new VendorPayment 

    when: 
    withFilters(action:"buy") { 
    controller.buy() 
    } 

    then: 
    VendorPayment.count() == 1 
} 
+2

Perché è necessario prendere in giro il metodo 'get'? Stai già prendendo in giro 'Person' nel test e creando un'istanza Person derisa. Lascia che GORM faccia il suo lavoro nel prendere l'istanza della Persona richiesta all'interno di 'show'? Aiuta? – dmahapatro

+0

Ho provato a creare un semplice esempio di dover prendere in giro il metodo 'get' e forse quell'esempio era troppo semplicistico. Modificherò la domanda per aggiungere una situazione più coinvolgente. –

risposta

11

Come dmahaptro detto, non lo fai necessità per deridere la vostra persona . Il metodo preferito/corretto per catturare un oggetto in un test tramite .get() è il modo in cui lo si possiede: si crea l'istanza del dominio e si passa l'id all'azione del controller per il consumo.

Tuttavia, se avete bisogno di prendere in giro un metodo statico come questo ci sono due approcci:

1) Utilizzare il metodo mockFor fornito da GrailsUnitTestMixin e quindi specificare il metodo statico in questo modo:

GrailsMock mockPerson = mockFor(Person) 
mockPerson.demand.static.get() { Long id -> new Person(name:"John") } 

Nota che nel tuo caso il metodo statico get non conteneva il tipo di parametro corretto (int è stato utilizzato invece di Long) quindi questo metodo non è stato richiamato.

2) Modificare la classe 'metaClass. La modifica della meta class è altamente sconsigliata (specialmente quando l'annotazione @Mock già imposta tutto per te) e può portare a perdite di test (cioè, altri test non funzioneranno correttamente dopo aver modificato la metaClass, a meno che non si ripristini il metaclass originale dopo il test è fatto).

Detto questo, per il bene di familiarità, il modo in cui si sarebbe deridere il metodo .get() è via:

Person.metaClass.static.get = { Long id -> return personWithId }

Ancora una volta, questo è considerato cattiva pratica e dovrebbe essere evitata, ma il gioco è fatto .

+0

Apprezzo la risposta e in particolare la guida sulle "migliori pratiche" relative al test sui graal. Farò in modo di evitare di dover fare affidamento su questo tipo di metodo statico, tuttavia, l'utilizzo di quanto segue nel blocco di installazione produce ancora un test non riuscito (model.person è nullo): 'Person.metaClass.static.get = {id -> new Person (nome: "John")} Anche questo non funziona: 'Person.metaClass.static.get = {id -> nuova persona (nome:" John "). save()}' Perché questo non funziona ancora in questo caso? –

+0

@Brice Come fallisce? Ho aggiunto un tipo al parametro 'id', che potrebbe essere d'aiuto. – Igor

+0

Sì! La mancanza di tipo sul parametro mi stava uccidendo! Così ora funziona e inoltre funziona anche: 'mockFor (Person) .demand.static.get() {ID lungo -> nuova persona (nome:" John ")}' –