Grails: Come si fa a unità di prova di un oggetto di comando con un servizio iniettato in esso
-
19-09-2019 - |
Domanda
Sto cercando di testare un controller che ha un oggetto Command con l'associazione di dati.
l'oggetto Command ha un servizio iniettato in esso.
Ma quando provo testare il comando oggetto del metodo di servizio iniettato non si trova mai in quanto non è mai "iniettata"
C'è un modo per deridere un servizio all'interno di un oggetto di comando?
Metodo di prova
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')
}
}
L'oggetto Command ha una "userService" iniettato in esso.
Il validatore chiama questa 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)
}
}
Soluzione
iniezione servizio viene fatto usando Primavera autowire per nome. (Grep l'albero dei sorgenti Grails per autowire
di trovare un bel frammento di codice è possibile utilizzare per farlo autowire i controller per voi in test di integrazione). Questo funziona solo in test di integrazione, dove c'è un contesto di applicazione della molla intorno che ha i fagioli che può essere iniettata.
In unit test, dovete farlo da soli perché non c'è Primavera-terreno circostante la tua roba. Questo può essere un dolore, ma ti dà alcuni vantaggi:
1) E 'facile per iniettare versioni finto di servizi - per esempio, utilizzando un Expando
- al fine di specificare più da vicino il comportamento dei servizi che collaborano del controller, e per permettere di testare solo la logica di controllo piuttosto che il controllore e servizio insieme. (Si può certamente fare il secondo in una prova di unità pure, ma si ha la scelta di come legare in su.)
2) Ti costringe a essere esplicito circa le dipendenze del vostro controller - se si dipende da esso, i test mostreranno esso. Questo li rende una specifica meglio il comportamento del controllore.
3) Si può prendere in giro solo i pezzi di collaboratori esterni il controller dipende. Questo aiuta i test siano meno fragili -. Meno probabilmente bisogno di cambiare quando le cose cambiano
Risposta breve: il tuo metodo di prova ha bisogno di una linea di cmd.userService = userService
Altri suggerimenti
Ciò che Giovanni 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