Fala-se muito que com a utilização do ASP.NET MVC ganhamos maior controle sobre o projeto, existe facilidade na escrita e manutenção de testes, etc.
Mas, afinal de contas: Como é que eu testo os meus controllers?
Essa é uma questão que não é muito falada, e por vezes nem muito exercitada. Neste primeiro post sobre o assunto vou falar um pouco sobre como testar controllers que trabalham puramente com dados, ou seja, quero enviar dados para uma fonte de dados, garantir que o controller está usando ModelState de forma correta, etc.
Vou omitir algumas coisas aqui para manter o post breve (vou detalhar apenas os testes), mas o código fonte está completo e disponível: TestesControllerI.zip
Cenário 1: Controller recebe dados da view para adicionar na fonte de dados.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| [TestMethod]
public void E_UsuarioCriar_Valido_Entao_Adiciona_Ao_Repositório_E_Retorna_View_Sucesso()
{
var controller = new CriarUsuarioController();
var repositorio = new Mock<IRepositorioUsuarios>();
controller.RepositorioDeUsuarios = repositorio.Object;
var usuarioCriar = new UsuarioCriar
{
Nome = "Nome",
Login = "Login",
Senha = "Senha"
};
var result = controller.Criar(usuarioCriar);
repositorio
.Verify(r => r.Adicionar(It.Is<Usuario>(u => Iguais(u, usuarioCriar))), Times.Once());
Assert.AreEqual("Sucesso", result.ViewName);
} |
O que fazemos neste teste é bastante simples. Primeiro instanciamos nosso controller, linha 4.
Após isso, fazemos um mock no nosso repositório, linha 5. (não sabe o que é mock? Veja aqui, e aqui).
Criado nosso mock, dizemos ao controller que é para utilizar este repositório de dados, linha 6. Reparem que poderíamos fazer isso via construtor, mas ainda assim estamos IoC!
Na linha 8 criamos uma instância do ViewModel de criação de usuários. Estou utilizando um ViewModel pois com ele eu faço uso dos DataAnnotations para validar esses dados, sem sujar minhas entidades. E também para representar os dados da View, afinal, poderia ter um campo de confirmação de senha, captcha, etc, e isso não tem relação com minha entidade Usuario.
Na linha 14 fazemos a chamada para nossa action, passando o ViewModel como parâmetro. Notem que eu tenho um retorno da minha action, e atribuí para a variável chamada result.
Na linha 16 fazemos a verificação do nosso mock. Neste caso estou verificando se o método Adicionar foi chamado, e recebeu como parâmetro um Usuario, com as propriedades iguais as do ViewModel. Isso para nós quer dizer que o controller soube mapear um ViewModel para um Usuario e então chamou o repositório passando este usuário.
Por fim, na linha 19 verifico se a View retornada é a view com nome “Sucesso”, isto por que este teste diz que para um ViewModel preenchido corretamente, deve ser adicionado no repositório e então a view de sucesso deve ser exibida.
Simples não é? Vamos garantir o cenário inverso agora:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| [TestMethod]
public void E_UsuarioCriar_Inalido_Entao_Nao_Adiciona_Ao_Repositório_E_Retorna_View_CriarUsuario()
{
var controller = new CriarUsuarioController();
var repositorio = new Mock<IRepositorioUsuarios>();
controller.RepositorioDeUsuarios = repositorio.Object;
controller.ModelState.AddModelError("Senha", "Senha deve ser preenchida");
var usuarioCriar = new UsuarioCriar
{
Nome = "Teste",
Login = "Login",
Senha = null
};
var view = controller.Criar(usuarioCriar);
repositorio
.Verify(r => r.Adicionar(It.IsAny<Usuario>()), Times.Never());
Assert.AreEqual("CriarUsuarioView", view.ViewName);
Assert.AreEqual(usuarioCriar, view.ViewData.Model);
} |
O código é quase igual ao da listagem anterior. Então vamos direto para a linha 8. Na linha 8 o que fazemos é adicionar um erro ao ModelState do controller. Este erro fará com que a validação do ModelState tenha o resultado inválido, mudando assim o comportamento do nosso controller.
Na linha 18 fazemos a verificação do nosso mock, garantindo que o método Adicionar não foi chamado nenhuma vez.
Nas linhas 21 e 22 fazemos os asserts de que a view retornada é a view de cadastro, pois os dados precisam ser corrigidos, e que a ViewModel retornada é a mesma que foi enviada para o controller.
Bom pessoa, para essa primeira parte é basicamente isso. São testes simples, que até podem parecer bobos, mas que nos ajudam a ter controle total sobre nosso código. Com esse tipo de testes conseguimos melhorar a escalabilidade de nossos sistemas MVC, garantimos que nossos controllers fazem apenas aquilo que devem fazer e conseguimos ter um pouco mais de segurança em nossas implementações futuras.
Estes testes podem ser refatorados, afinal a criação do controller, mock de repositório e configuração do repositório no controller se repetem nos dois testes. Isso pode ser feito então através de um TestInitialize. Fica aí o exercício para vocês.
No próximo post veremos como testar melhor retornos de views, redirects e retorno de dados.
Abraços,
Vinicius Quaiato.