성배 : 서비스가 주입 된 명령 개체를 어떻게 테스트합니까?
-
19-09-2019 - |
문제
데이터 바인딩이있는 명령 개체가있는 컨트롤러를 테스트하려고합니다.
명령 객체에 서비스가 주입되어 있습니다.
그러나 명령 개체를 테스트하려고 할 때 주입 된 서비스 방법은 결코 "주입"되지 않으므로 결코 발견되지 않습니다.
명령 개체 내부에서 서비스를 조롱하는 방법이 있습니까?
시험 방법
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
}
사용자 서비스의 getUser () 메소드는 찾을 수 없습니다.
Cannot invoke method getUser() on null object
java.lang.NullPointerException: Cannot invoke method getUser() on null object
암호
호출되는 컨트롤러의 로그인 메소드
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')
}
}
명령 객체에는 "userservice"가 주입되었습니다.
유효성 검사기는이 사용자 서비스를 호출하여 사용자를 찾습니다
class LoginCommand {
def userService
String login
String password
static constraints = {
login blank:false, validator:{ val, cmd ->
if(!cmd.userService.getUser()){
return "user.not.found"
}
}
}
userervice.getUser ()는 다음과 같습니다.
class UserService {
boolean transactional = true
User getUser(String login) {
return User.findByLogin(login)
}
}
해결책
서비스 주입은 Spring Autowire-by-Name을 사용하여 수행됩니다. (성배 소스 트리를 grep autowire
좋은 코드 조각을 찾으려면 통합 테스트에서 컨트롤러를 자동으로 사용하여 사용할 수 있습니다.) 이것은 주변에 주입 할 수있는 콩이있는 스프링 응용 프로그램 컨텍스트가있는 통합 테스트에서만 기능합니다.
단위 테스트에서는 물건을 둘러싼 스프링 랜드가 없기 때문에 직접해야합니다. 이것은 고통 일 수 있지만 몇 가지 이점을 제공합니다.
1) Mock 버전의 서비스를 주입하는 것은 쉽습니다. 예를 들어 Expando
- 컨트롤러의 협업 서비스의 동작을보다 면밀히 지정하고 컨트롤러 및 서비스 대신 컨트롤러 로직 만 테스트 할 수 있도록합니다. (단위 테스트에서도 후자를 할 수 있지만이를 연결하는 방법을 선택할 수 있습니다.)
2) 컨트롤러의 종속성에 대해 명시 적으로 강요합니다. 이에 의존하면 테스트에 표시됩니다. 따라서 컨트롤러의 동작에 대한 더 나은 사양이됩니다.
3) 컨트롤러가 의존하는 외부 공동 작업자 만 조롱 할 수 있습니다. 이를 통해 테스트가 깨지기 쉬운 데 도움이됩니다. 상황이 바뀔 때 변화가 필요하지 않습니다.
짧은 답변 : 테스트 방법이 필요합니다 cmd.userService = userService
선.
다른 팁
요한이 말하는 것은 마크에 있습니다. 한 가지 예는 다음과 같습니다.
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
물건을 조롱하는 다른 방법을 조회 할 수 있습니다 http://groovy.codehaus.org/groovy+mocks