2014-04-28 25 views
5

Używam Grails 2.3.7. Mam proste obiektu domeny Osoba:Wyśmiewanie statycznej metody get gorm w teście jednostek Grailsa

class Person { 
    String name 
    static constraints = {} 
} 

i kontrolera:

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

próbuję napisać badanej jednostki dla kontrolera do wykonywania metody show, ale nie jestem pewien, w jaki sposób trzeba skonfigurować test, aby upewnić się, że wywołanie statyczne Person.get(id) zwraca wartość.

Oto moje (braku) próba:

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

Ten test nie powiedzie się, ponieważ warunek model.person != null zawiedzie. Jak przerobić ten test, aby go przekazać?

EDIT

W tym przypadku mogę bok kroku statyczne wywołanie metody przez przerabianiu test wygląda następująco:

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

jednak naprawdę chciałbym, aby zrozumieć, w jaki sposób poprawnie mock Person ' s statyczna metoda get.

EDIT 2

Oto kolejna sytuacja zademonstrować moje postrzeganą potrzebę mock metodę get. Biorąc pod uwagę ten kod: Filtr

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

Próbuję sprawdzić, czy filtr ten działa zgodnie z oczekiwaniami za pomocą następującego testu:

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

Dlaczego wymagane jest sfałszowanie metody "get"? Już wyśmiewasz "Osobę" w teście i tworzysz fałszywą instancję Osoby. Czy GORM wykonuje swoją pracę w celu pobrania wymaganej instancji Person wewnątrz "show"? Pomaga? – dmahapatro

+0

Próbowałem ugotować prosty przykład potrzeby wyśmiewania metody 'get' i być może ten przykład był zbyt uproszczony. Będę edytować pytanie, aby dodać bardziej zaangażowaną sytuację. –

Odpowiedz

11

Podoba dmahaptro wspomniano, nie potrzebę wyśmiewać się z osobą . Preferowany/właściwy sposób przechwytywania obiektu w teście za pomocą .get() jest taki, jaki masz - tworzysz instancję domeny i przekazujesz jej identyfikator do akcji kontrolera w celu zużycia.

Jednak jeśli trzeba mock statycznej metody takie jak to istnieją dwa podejścia:

1) Użyj metody mockFor dostarczone przez GrailsUnitTestMixin a następnie określić metodę statyczną tak:

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

Zauważ, że w twoim przypadku metoda statyczna get nie zawiera poprawnego parametru parametru (int został użyty zamiast Long), więc ta metoda nie była wywoływana.

2) Zmodyfikuj klasę "metaClass. Zmiana metaClass jest wysoce odradzana (szczególnie, gdy adnotacja @Mock już wszystko ustawia dla ciebie) i może prowadzić do testowania przecieków (tj. Inne testy nie będą działały poprawnie po zmianie metaklasy, chyba że przywrócisz oryginalną metaklasę po test jest zakończony).

Mimo to, ze względu na znajomości, tak byś mock metoda .get() jest poprzez:

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

Ponownie, jest to uważane za złą praktyką i należy unikać, ale nie trzeba go .

+0

Doceniam odpowiedź, a zwłaszcza wskazówki dotyczące "najlepszych praktyk" dotyczących testowania grails. Upewnię się, aby nie polegać na szyderstwie tego typu statycznej metody, jednak użycie poniższego w bloku konfiguracji nadal powoduje test niepowodzenia (model.person ma wartość null): 'Person.metaClass.static.get = {id -> new Person (name: "John")} ' To również nie działa: ' Person.metaClass.static.get = {id -> new Person (name: "John"). save()} ' Dlaczego w tym przypadku nadal nie działa? –

+0

@Brice Jak to się nie udaje? Dodałem typ do parametru "id", co może pomóc. – Igor

+0

Tak! Brak typu na parametrze zabijał mnie! Teraz to działa i dodatkowo działa to również: 'mockFor (Person) .demand.static.get() {Long id -> new Person (name:" John ")}' –