Grails: Comment l'unité testez-vous un objet de commande avec un service injecté dans ce
-
19-09-2019 - |
Question
Je suis en train de tester un contrôleur qui a un objet de commande avec liaison de données.
L'objet de commande a un service injecté en elle.
Mais quand j'essaie de tester la commande objet de la méthode de service injecté est jamais trouvé qu'il est jamais « injecté »
Y at-il un moyen de se moquer d'un service à l'intérieur d'un objet de commande?
Méthode d'essai
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
}
La méthode getUser () de la UserService ne se trouve pas
Cannot invoke method getUser() on null object
java.lang.NullPointerException: Cannot invoke method getUser() on null object
code
La méthode de connexion de l'unité de commande étant appelée,
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'objet de commande a une "UserService" injecté en elle.
Le validateur appelle cette UserService pour trouver un utilisateur
class LoginCommand {
def userService
String login
String password
static constraints = {
login blank:false, validator:{ val, cmd ->
if(!cmd.userService.getUser()){
return "user.not.found"
}
}
}
Le userService.getUser () ressemble à ceci.
class UserService {
boolean transactional = true
User getUser(String login) {
return User.findByLogin(login)
}
}
La solution
injection de service est effectuée à l'aide par nom autowire-Spring. (GREP l'arbre source Grails pour autowire
trouver un beau fragment de code que vous pouvez utiliser pour obtenir à autowire vos contrôleurs pour vous dans les tests d'intégration.) Cela ne fonctionne que dans les tests d'intégration, où il y a un contexte d'application Spring autour qui a les haricots peut être injecté.
Dans les tests unitaires, vous devez faire vous-même car il n'y a pas de printemps-terre entourant vos affaires. Cela peut être une douleur, mais vous donne quelques avantages:
1) Il est facile d'injecter des versions fausses de services - par exemple, en utilisant un Expando
- afin de préciser davantage le comportement des services de collaboration de votre contrôleur, et pour vous permettre de tester uniquement la logique de commande plutôt que le contrôleur et service ensemble. (Vous pouvez certainement faire ce dernier dans un test unitaire aussi bien, mais vous avez le choix de la façon de le câbler.)
2) Il vous oblige à être explicite sur les dépendances de votre contrôleur - si vous dépendez, vos tests montrer. Cela leur fait une meilleure spécification pour le comportement de votre contrôleur.
3) Vous pouvez seulement se moquer des morceaux de collaborateurs externes de votre contrôleur dépend. Cela aide vos tests soient moins fragiles -. Moins susceptibles d'avoir besoin de changer quand les choses changent
Réponse courte: votre méthode de test a besoin d'une ligne de cmd.userService = userService
Autres conseils
Ce que dit John est sur la marque. Un exemple pourrait être:
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
Vous pouvez rechercher d'autres façons d'objets fantaisie http://groovy.codehaus.org/Groovy+Mocks