VRaptor

Testando Componentes e Controllers

Fazer testes de unidade com o VRaptor 4 na maioria das vezes é simples, não fugindo muito dos testes de unidade de uma classe qualquer.

Todo o controle de Injeção de Dependência é feito através do CDI e para melhorar a testabilidade de suas classes aconselhamos a injeção de dependências por construtor, dessa forma você pode trabalhar com mocks facilmente, como por exemplo:

@Controller
public class PessoaController {

    private final Result result;
    private final Validator validator;

    /**
     * @deprecated CDI eyes only
     */
    protected PessoaController() {
        this(null, null);
    }

    @Inject
    public PessoaController(Result result, Validator validator) {
        this.result = result;
        this.validator = validator;
    }
    
}

Para testar esse controller, podemos usar o MockResult e MockValidator como dependência:

public class PessoaControllerTest {

    private MockResult result;
    private MockValidator validator;
    private PessoaController controller;
    
    @Before
    public void setUp() {
        result = new MockResult();
        validator = new MockValidator();
        controller = new PessoaController(result, validator);
    }
    
}

O que acontece quando fazemos a injeção de dependência via atributo, é que precisaremos do contexto do CDI e o mesmo não é suportado pelo JUnit, visto que esse cria todo um ambiente para os testes e não levanta esse contexto. No entanto há soluções para que você possa dentro do seu caso de teste subir o contexto CDI e assim usufruir da injeção de dependência, controle transacional e tudo aquilo que você precisa do CDI.

Result e Validator são componentes quase sempre presentes em seus Controllers, para facilitar seus testes o VRaptor fornece mocks para essas implementações.

MockResult

O MockResult ignora os redirecionamentos que você fizer, e acumula os objetos incluídos, para você poder inspecioná-los e fazer as suas asserções.

Considere o seguinte método da classe PessoaController:

@Post("/pessoa/adiciona")
public void adiciona(Pessoa pessoa) {
    validator.addIf(pessoa.getNome() == null, new SimpleMessage("nome", "O nome deve ser preenchido"));
    validator.onErrorRedirectTo(IndexController.class).index();

    result.include("success", "Incluído com sucesso.");
    
    result.redirectTo(IndexController.class).index();
}

Para testá-lo você pode em sua classe PessoaControllerTest fazer algo como:

@Test
public void deveConterMensagemDeSucesso() {
    Pessoa pessoa = new Pessoa();
    pessoa.setNome("Renan Montenegro");
    
    controller.add(pessoa);
    
    Assert.assertTrue(result.included().containsKey("success"));
    Assert.assertEquals("Incluído com sucesso.", result.included("success"));
}

Note que qualquer chamada do tipo result.use(...) será ignorada.

MockSerializationResult

De forma bastante semelhante, você pode utilizar o MockSerializationResult sempre que quiser inspecionar o conteúdo serializado no Result.

Na sua classe PessoaControllerTest o result deve ser do tipo MockSerializationResult, como a seguir:

private MockSerializationResult result;

Considere o seguinte método da classe PessoaController:

@Post("/pessoa/serializa")
public void serializa(Pessoa pessoa) {
    result.use(Results.json()).from(pessoa).serialize();
}

No seu teste você pode fazer algo como:

@Test
public void deveriaSerializarPessoa() {
    Pessoa pessoa = new Pessoa();
    pessoa.setNome("Renan Montenegro");
    
    controller.serializa(pessoa);
    
    Assert.assertEquals("{\"pessoa\": {\"nome\": \"Renan Montenegro\"}}", result.serializedResult());
}

MockValidator

O MockValidator vai executar a validação acumulando possíveis erros. Para recuperá-los você pode usar o método getErrors() como você pode ver no exemplo:

@Test
public void deveLancarValidationException() {
    try {
        controller.add(new Pessoa());
        Assert.fail();
    } catch (ValidationException e) {
        List<Message> errors = e.getErrors();
        Assert.assertTrue(errors.contains(new SimpleMessage("nome", "O nome deve ser preenchido")));
        Assert.assertEquals(1, errors.size());
    }
}

A chamada do método validator.onErrorUse, no caso de problema de validação, irá gerar uma ValidationException, que pode ser usado como um resultado esperado pelo seu teste, como a seguir:

@Test(expected = ValidationException.class)
public void deveLancarValidationException() {
    controller.add(new Pessoa());
}