Grails: ¿Cómo unidad de prueba de un objeto de comando con un servicio inyectada en ella
-
19-09-2019 - |
Pregunta
Estoy tratando de probar un controlador que tiene un objeto de comando con el enlace de datos.
El objeto de comando tiene un servicio inyectado en ella.
Pero cuando intento probar el comando objeto el método de servicio inyectada nunca se encuentra, ya que nunca se "inyecta"
¿Hay una manera de burlarse de un servicio dentro de un objeto de comando?
Método de ensayo
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
}
No se encuentra el método getUser () de la UserService
Cannot invoke method getUser() on null object
java.lang.NullPointerException: Cannot invoke method getUser() on null object
Código
El método de inicio de sesión del controlador que se llama,
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')
}
}
El objeto de comando tiene un "UserService" inyectado en él.
El validador llama a esto UserService para encontrar un usuario
class LoginCommand {
def userService
String login
String password
static constraints = {
login blank:false, validator:{ val, cmd ->
if(!cmd.userService.getUser()){
return "user.not.found"
}
}
}
El userService.getUser () se parece a esto.
class UserService {
boolean transactional = true
User getUser(String login) {
return User.findByLogin(login)
}
}
Solución
Servicio de inyección se realiza utilizando Spring Autowire por nombre. (GREP el árbol de fuentes Grails para autowire
para encontrar un fragmento de código agradable que puede utilizar para conseguir que Autowire sus controladores para que en las pruebas de integración.) Esto sólo funciona en las pruebas de integración, donde hay un contexto de aplicación de primavera en torno a que tiene los granos que se puede inyectar.
En las pruebas de unidad, que tiene que hacer esto por sí mismo ya que no hay primavera-tierra que rodea su materia. Esto puede ser un dolor, pero le da algunas ventajas:
1) Es fácil de inyectar versiones simuladas de servicios - por ejemplo, usando un Expando
- con el fin de especificar más de cerca el comportamiento de los servicios de colaboración de su controlador, y permitirá poner a prueba únicamente la lógica del controlador en lugar del controlador y mantienen juntas. (Por supuesto que puede hacer esto último en una prueba de unidad, así, pero usted tiene la opción de cómo cablear.)
2) Se obliga a ser explícito acerca de las dependencias de su controlador - si depende de él, sus pruebas mostrarán la misma. Esto los convierte en una mejor especificación del comportamiento de su controlador.
3) Se puede burlarse sólo las piezas de colaboradores externos su controlador depende. Esto ayuda a que las pruebas sean menos frágiles -. Menos probable que tenga que cambiar cuando cambian las cosas
Respuesta corta: el método de prueba necesita una línea cmd.userService = userService
Otros consejos
Lo que Juan dice es en la marca. Un ejemplo podría ser:
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
Se puede buscar otras formas de objetos simulados en http://groovy.codehaus.org/Groovy+Mocks