2009-11-09 13 views
5

Sto provando a testare un controller con un oggetto Command con associazione dati.Grails: Come si esegue il test dell'unità su un oggetto comando con un servizio immesso in esso

L'oggetto comando ha un servizio immesso in esso.

ma quando provo testare il comando Oggetto il metodo del servizio iniettato non è mai trovato come non è mai "iniettata"

C'è un modo per deridere un servizio all'interno di un oggetto di comando?

Metodo

void testLoginPasswordInvalid() { 
    mockRequest.method = 'POST' 
    mockDomain(User, [new User(login:"freddy", password:"realpassword")]) 
    mockLogging(UserService) // userService mocked 
    MockUtils.prepareForConstraintsTests(LoginCommand) 

    def userService = new UserService() 
    def user = userService.getUser("freddy")//Gets called and returns the mockDomain 
    assert userService.getUser("freddy")//Passes 

    def cmd = new LoginCommand(login:"freddy", password:"letmein") 
    cmd.validate() // Fails (userService is nevr injected) 
    controller.login(cmd) 
    assertTrue cmd.hasErrors() 
    assertEquals "user.password.invalid", cmd.errors.password 
    assertEquals "/store/index", renderArgs.view 
} 

Il metodo getUser() della userService non si trova

Cannot invoke method getUser() on null object 
java.lang.NullPointerException: Cannot invoke method getUser() on null object 

Codice

Il metodo di login del controller di essere chiamato,

def login = { LoginCommand cmd -> 
    if(request.method == 'POST') { 
    if(!cmd.hasErrors()){ 
     session.user = cmd.getUser() 
     redirect(controller:'store') 
    } 
    else{ 
     render(view:'/store/index', model:[loginCmd:cmd]) 
    } 
    }else{ 

    render(view:'/store/index') 
    } 
} 

All'oggetto comando è stato inserito un "userService".

Il validatore chiama questo userService per trovare un utente

class LoginCommand { 

    def userService 

    String login 
    String password 

    static constraints = { 
     login blank:false, validator:{ val, cmd -> 
      if(!cmd.userService.getUser()){ 
      return "user.not.found" 
      } 
     } 
} 

L'userService.getUser() assomiglia a questo.

class UserService { 

    boolean transactional = true 

    User getUser(String login) { 
     return User.findByLogin(login) 

    } 
} 

risposta

11

L'iniezione di servizio viene eseguita utilizzando Spring autowire-by-name. (Grep the Grails, l'albero dei sorgenti per autowire, per trovare un bel frammento di codice che puoi utilizzare per ottenere automaticamente i tuoi controller per i test di integrazione.) Funziona solo nei test di integrazione, dove c'è un contesto di applicazione Spring che ha i bean che può essere iniettato

Nei test di unità, devi farlo da solo perché non c'è una terra di primavera che circonda le tue cose. Questo può essere un problema, ma offre alcuni vantaggi:

1) È facile iniettare versioni di servizi fittizi, ad esempio utilizzando un Expando, per specificare più da vicino il comportamento dei servizi di collaborazione del controller e per consente di testare solo la logica del controller anziché il controller e il servizio insieme. (Puoi certamente fare anche quest'ultimo in un test unitario, ma puoi scegliere come collegarlo).

2) Ti costringe ad essere esplicito sulle dipendenze del tuo controller - se dipendi da questo, i tuoi test lo mostreranno. Questo li rende una specifica migliore per il comportamento del controller.

3) È possibile prendere in giro solo i pezzi di collaboratori esterni da cui dipende il controller. Questo aiuta i tuoi test a essere meno fragili - meno probabilità di dover cambiare quando le cose cambiano.

Risposta breve: il metodo di test richiede una riga cmd.userService = userService.

+0

Grazie, questo ha funzionato a meraviglia e ora capisco più – Daxon

8

Quello che John dice è sul marchio.Un esempio potrebbe essere:

def mockUsers = [new User(login:"freddy", password:"realpassword")] 
mockDomain(User, mockUsers) 

def userService = [getUser:{String login -> mockUsers[0]}] as UserService 

def cmd = new LoginCommand (/*arguments*/) 
cmd.userService = userService 

si può cercare altri modi per oggetti mock al http://groovy.codehaus.org/Groovy+Mocks