Pergunta

Eu estou tentando testar um controlador que tem um objeto de comando com a ligação de dados.

O objeto de comando tem um serviço injetado.

Mas quando eu tento testar o objeto de comando o método de serviço injetados nunca é encontrado como nunca é "injetado"

Existe uma maneira de zombar um serviço dentro de um objeto de comando?

Método de ensaio

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
}

O método getUser () do UserService não é encontrada

Cannot invoke method getUser() on null object
java.lang.NullPointerException: Cannot invoke method getUser() on null object

Código

O método de login do controlador sendo chamado,

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')
  }
}

O objeto de comando tem um "UserService" injetado.

O validador chama este UserService para encontrar um usuário

 class LoginCommand {

    def userService

    String login
    String password

    static constraints = {
      login blank:false, validator:{ val, cmd ->
          if(!cmd.userService.getUser()){
             return "user.not.found"
          }
      }
 }

O userService.getUser () se parece com isso.

class UserService {

    boolean transactional = true

    User getUser(String login) {
        return User.findByLogin(login)

    }
}
Foi útil?

Solução

injeção

O serviço é feito usando Spring autowire-by-name. (Grep a árvore fonte Grails para autowire para encontrar um fragmento de código agradável você pode usar para obtê-lo para autowire seus controladores para você em testes de integração.) Isso só funciona em testes de integração, onde há um contexto de aplicativo Spring em torno de que tem o feijão que pode ser injetado.

Em testes de unidade, você tem que fazer isso sozinho já que não há Spring-terreno circundante suas coisas. Esta pode ser uma dor, mas dá-lhe alguns benefícios:

1) É fácil para injetar versões simuladas de serviços - por exemplo, usando um Expando - para mais de perto especificar o comportamento de serviços de colaboração do seu controlador, e para permitir que você testar apenas a lógica do controlador em vez do controlador e juntamente serviço. (Você pode certamente fazer o último em um teste de unidade, bem como, mas você tem a escolha de como conectar-se.)

2) Obriga-nos a ser explícito sobre as dependências de seu controlador - se depender dele, seus testes irão mostrar isso. Isso os torna uma especificação melhor para o comportamento de seu controlador.

3) Você pode zombar apenas os pedaços de colaboradores externos seu controlador depende. Isso ajuda a seus testes de ser menos frágil -. Menos propensos a necessidade de mudança quando as coisas mudam

Resposta curta:. O seu método de teste precisa de uma linha cmd.userService = userService

Outras dicas

O que João diz é sobre a marca. Um exemplo poderia 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

Você pode consultar outras formas de objetos mock em http://groovy.codehaus.org/Groovy+Mocks

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top