2014-04-28 6 views
5

Ich benutze Grails 2.3.7. Ich habe ein einfaches Person Domain-Objekt:Mocking statisch Get Gorm-Methode in Grails Unit-Test

class Person { 
    String name 
    static constraints = {} 
} 

Und 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] 
    } 
    ... 
} 

Ich versuche, einen Komponententest zu schreiben für die Steuerung der show Verfahren auszuüben, aber ich bin nicht sicher, wie ich Sie müssen den Test konfigurieren, um sicherzustellen, dass der statische Aufruf Person.get(id) einen Wert zurückgibt.

Hier ist meine (Fehler) Versuch:

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" 
    } 
} 

Dieser Test schlägt fehl, da die Bedingung model.person != null ausfällt. Wie schreibe ich diesen Test um, um ihn zu bestehen?

EDIT

In diesem Fall kann ich die statischen Methodenaufruf neben Schritt für den Test Nacharbeiten thusly:

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" 
} 

Aber ich würde wirklich verstehen möchte, wie man richtig die verspotten Person ' s statische get Methode.

EDIT 2

Hier ist eine andere Situation meine empfundene Notwendigkeit zu zeigen, die get Methode zu verspotten. Vor diesem Filtercode:

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

Ich versuche, dass diese Filter zu testen, funktionieren wie mit dem folgenden Test zu erwarten:

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

Warum ist es erforderlich, "Get" -Methode zu verspotten? Sie verspotten bereits "Person" im Test und erstellen eine verspottete Personeninstanz. Lasse GORM seine Arbeit erledigen, indem er die erforderliche Person-Instanz in 'show' aufnehme? Hilft? – dmahapatro

+0

Ich habe versucht, ein einfaches Beispiel dafür zu erstellen, dass ich die "Get" -Methode verspotten muss, und vielleicht war dieses Beispiel zu einfach. Ich werde die Frage bearbeiten, um eine kompliziertere Situation hinzuzufügen. –

Antwort

11

Wie dmahaptro erwähnte, nicht wahr Notwendigkeit Ihre Person zu verspotten . Die bevorzugte Art, ein Objekt in einem Test über .get() zu erfassen, ist die Art und Weise, wie Sie es haben - Sie erstellen die Domäneninstanz und übergeben ihre ID an die Controller-Aktion für den Verbrauch.

Wenn Sie jedoch eine statische Methode wie folgt verspotten müssen gibt es zwei Ansätze:

1) Verwenden Sie die mockFor Methode von GrailsUnitTestMixin zur Verfügung gestellt und dann die statische Methode angeben wie folgt:

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

Beachten Sie, dass die statische Methode get des Mocks in Ihrem Fall nicht den richtigen Parametertyp enthielt (int wurde anstelle von Long verwendet), sodass diese Methode nicht aufgerufen wurde.

2) Ändern Sie die Klasse 'metaClass. Von einer Änderung der MetaClass wird dringend abgeraten (besonders, wenn die Annotation @Mock bereits alles für Sie vorgibt), was zu Testlecks führen kann (dh andere Tests funktionieren nicht richtig, nachdem Sie die MetaClass geändert haben, außer Sie stellen die ursprüngliche Metaklasse wieder her der Test ist erledigt).

Das heißt, aus Gründen der Vertrautheit, die Art und Weise würden Sie die .get() Methode verspotten ist über:

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

Auch diese schlechte Praxis betrachtet und sollte vermieden werden, aber da haben Sie es .

+0

Ich schätze die Antwort und vor allem die Anleitung zu den "Best Practices" bezüglich der Testung von Grails. Ich werde sicherstellen, dass ich mich nicht darauf verlassen muss, diese Art von statischen Methoden zu verspotten, jedoch führt die Verwendung des folgenden im Setup-Block immer noch zu einem fehlgeschlagenen Test (model.person ist null): 'Person.metaClass.static.get = {id -> neue Person (Name: "John")} ' Dies schlägt auch fehl: ' Person.metaClass.static.get = {ID -> neue Person (Name: "John"). save()} ' Warum scheitert dies in diesem Fall noch? –

+0

@Brice Wie schlägt es fehl? Ich habe dem ID-Parameter einen Typ hinzugefügt, der hilfreich sein kann. – Igor

+0

Ja! Der Mangel an Typ auf dem Parameter hat mich getötet! Jetzt funktioniert das und zusätzlich funktioniert das auch: 'mockFor (Person) .demand.static.get() {Lange ID -> neue Person (Name:" John ")}' –