quarta-feira, 28 de setembro de 2011

Como testar os Controllers do Vraptor com Mockito


Um dos frameworks que mais gosto de trabalhar, devido a sua simplicidade e produtividade é o Vraptor. Uma das suas vantagens é que ele abstrai a complexidade da API de Servlets, NÃO te obriga a trabalhar com getters/setters evitando que seu modelo se torne anêmico. Não é intrusivo, você foca na sua lógica de negócios e não em aprender sobre a arquitetura e classes do framework, ou seja, a curva de aprendizado é minima.

Trabalha de forma muito fácil com injeção de dependência. Não é necessário configurar quase nada. Tem suporte a REST e inumeros outros recursos. Mas oque chama realmente a atenção é que você consegue começar a trabalhar com ele em questão de minutos. Poderiamos ficar o dia todo discutindo sobre as vantagens de utilizar este framework. Portanto vamos ao objetivo do post: Aprender a testar unitáriamente as classes de controller do Vraptor.

Para isso utilizaremos o JUnit e o Mockito.

Desejamos testar o método salva(Aluno aluno) do controller abaixo. Este método recebe um aluno, verifica se é valido, salva e redireciona para a listagem, caso o aluno seja inconsistente ele redireciona para a página de cadastro/edição para exibir os erros.

@Resource
public class AlunoController {
private AlunoRepository alunoRepository;

 private Result result;

 private Validator validator;

 public AlunoController(AlunoRepository alunoRepository, Result result, Validator validator) {
  this.alunoRepository = alunoRepository;
  this.result = result;
  this.validator = validator;
 }

 public void add(Aluno aluno) {
  this.validator.validate(aluno);
  this.validator.onErrorForwardTo(this).cadastra();

  this.alunoRepository.add(aluno);
  this.result.redirectTo(this).lista();
 }
}
Temos como dependência a interface AlunoRepository e como o objetivo é testar o controller, devemos mockar esta dependência.

Criamos uma classe de teste comum do JUnit.

public class AlunoControllerTest {
 private AlunoController controller;

 private AlunoRepository alunoRepository;

 private MockResult result;

 private JSR303MockValidator validator;

 @Before
 public void setUp() throws Exception {
  this.alunoRepository = mock(AlunoRepository.class);

  result = spy(new MockResult());
  validator = spy(new JSR303MockValidator());
  controller = new AlunoController(alunoRepository, turmaRepository, result, validator);
 }
}

Note que os objetos result e validator usam mocks apropriados, portanto não precisamos nos preocupar com eles. É melhor utilizar estes mocks fornecidos, pois estes objetos usam Fluent Interface oque torna sua testabilidade muito difícil.

Repare também que usamos o método spy(T object) para obter as instancias do result e validator, isso é util pois conseguimos chamar os métodos reais dos objetos, então podemos verificar se realmente houveram interações com os objetos.

O teste:

@Test
public void add() {
 Aluno aluno = getAlunoConsistente();
 controller.add(aluno);
 verify(alunoRepository).add(aluno);
}
Através deste teste tentamos salvar um aluno, que é um objeto consistente (válido). Então o aluno deve ser adicionado ao repositório, portanto nos asseguramos que o método add(Aluno aluno) do repositório foi chamado, isso é feito através da linha: verify(alunoRepository).add(aluno);

Para testarmos nossas validações, desenvolvemos um teste que espera que uma ValidationException seja lançada e nos asseguramos que não houveram interações com o repositório:
@Test(expected = ValidationException.class)
public void gravaAlunoInconsistenteTest() {
 controller.grava(getAlunoInconsistente());
 verifyNoMoreInteractions(alunoRepository);
}
Esse post teve origem em uma thread do GUJ que abri e recebi orientações do Lucas Cavalcanti: http://www.guj.com.br/java/243436-vraptor-e-mockito---testes-de-controllers


UPDATE:
Muitas vezes desejamos verificar se o nosso método fez um redirecionamento para uma view ou lógica específica. Então como o Lucas citou, podemos fazer:


AlunoController spy = Mockito.spy(alunoController); 
when(result.redirectTo(alunoController)).thenReturn(spy); 
this.alunoController.grava(aluno); 
verify(spy).lista();

Assim estamos nos assegurando que o método lista() foi chamado, ou seja, foi feito o redirecionamento.

Abraços