Grails: Wie testen Sie Einheit ein Befehlsobjekt mit einem Dienst in sie eingespritzt
-
19-09-2019 - |
Frage
Ich versuche, einen Controller zu testen das hat ein Command-Objekt mit Datenbindung.
Das Command-Objekt hat einen Dienst hinein injiziert.
Aber wenn ich testen, versuchen, den Befehl des injizierten Service-Methode Objekt ist nie wie es nie „injiziert“
gefundenGibt es eine Möglichkeit, einen Dienst in einem Befehl Objekt zu verspotten?
Testmethode
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
}
Der getUser () -Methode des Userservice nicht gefunden
Cannot invoke method getUser() on null object
java.lang.NullPointerException: Cannot invoke method getUser() on null object
Code
Das Anmeldeverfahren des Controller aufgerufen wird,
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')
}
}
Das Command-Objekt hat eine "Userservice" injiziert hinein.
Der Validator nennt diesen Userservice um einen Benutzer zu finden
class LoginCommand {
def userService
String login
String password
static constraints = {
login blank:false, validator:{ val, cmd ->
if(!cmd.userService.getUser()){
return "user.not.found"
}
}
}
Die userService.getUser () sieht wie folgt aus.
class UserService {
boolean transactional = true
User getUser(String login) {
return User.findByLogin(login)
}
}
Lösung
Service Einspritzung mit Spring autowire-by-Namen getan. (Grep die Grails-Source-Tree für autowire
einen schönen Codefragment finden Sie es verwenden, um Ihre Controller für Sie in Integrationstests autowire.) Das funktioniert aber nur in Integrationstests, wo es ein Spring-Anwendungskontext um, dass die Bohnen hat, dass injiziert werden kann.
In Unit-Tests, müssen Sie dies selbst tun, da es keinen Frühling Land Ihre Sachen umgibt. Dies kann ein Schmerz sein, aber gibt Ihnen einige Vorteile:
1) Es ist einfach Mock Versionen von Dienstleistungen zu injizieren - zum Beispiel eines Expando
mit - um genauer, um das Verhalten des Controllers kollaborierenden Dienste angeben, und ermöglicht es Ihnen, nur die Controller-Logik, anstatt die Steuerung zu testen und Service zusammen. (Sie können natürlich auch die letztere in einem Unit-Test tun, aber Sie haben die Wahl, wie es verkabeln.)
2) Es zwingt Sie über die Abhängigkeiten von dem Controller explizit zu sein - wenn Sie davon abhängen, Ihre Tests es zeigen. Das macht sie eine bessere Beschreibung für das Verhalten des Controllers.
3) Sie können nur die Stücke von externen Mitarbeitern verspotten Ihre Steuerung abhängt. Dies hilft, Ihre Tests weniger zerbrechlich sein -. Weniger wahrscheinlich ändern zu müssen, wenn die Dinge ändern
Kurze Antwort: Ihre Testmethode benötigt eine cmd.userService = userService
Zeile
Andere Tipps
Was John sagt, ist auf die Marke. Ein Beispiel könnte sein:
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
Sie können Nachschlag andere Wege, um Mock-Objekte unter http://groovy.codehaus.org/Groovy+Mocks