Palestra ASP.NET MVC – SSI2010

Filed Under (ASP.NET MVC, Boas Práticas) by Vinicius Quaiato on 03-09-2010

Tagged Under : , , ,

Fala galera, ontem realizei uma palestra sobre ASP.NET MVC no SSI2010 em São José do Rio Preto (ou como o pessoal chamou, Saint Joseph of Black River).

Abaixo seguem os slides utilizados na palestra:

Códigos de exemplos

Os códigos de exemplo utilizados podem ser baixados aqui: http://viniciusquaiato.com/files/codesamples/MVC/SSI2010 Exemplos.zip
(em breve publico no github)

Abraços,
Vinicius Quaiato

ASP.NET MVC: Testando controllers parte III

Filed Under (ASP.NET MVC, Boas Práticas, TDD) by Vinicius Quaiato on 02-09-2010

Tagged Under : , , , , , , ,

Fala galera, neste terceiro post sobre ASP.NET MVC e testes(post I e post II) vou mostrar como testar a action de um controller que realiza upload de arquivos.

Testar um controller que realiza upload de arquivos

Este não é o teste mais simples, mas também não é complexo. A particularidade aqui é que estou utilizando o Moq, e com ele não consigo dar “bypass” nas propriedades que eu não quero mockar, ou seja, preciso mockar toda a cadeia da chamada (se vocês tiverem uma forma mais simples, é só mandar).

Configurando arquivo Resources

Vamos lá, antes de codificar, eu adiciono um arquivo de recursos no projeto de testes e adiciono uma foto como resource:

O código de testes

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void Deve_Salvar_Uma_Imagem_Que_Foi_Postada()
{
    //Arrange
    var controllerFotos = new ControllerFotos();
 
    var encoding = new UTF8Encoding();
    var memoryStream = new MemoryStream();
    ArquivosPostados.ImagemPostada.Save(memoryStream, ImageFormat.Jpeg);
 
    postedFile = new Mock<HttpPostedFileBase>();
    postedFile.Setup(p => p.InputStream).Returns(memoryStream);
 
    var files = new Mock<HttpFileCollectionBase>();
    files.Setup(f => f[0]).Returns(postedFile.Object);
 
    var request = new Mock<HttpRequestBase>();
    request.Setup(r => r.Files).Returns(files.Object);
 
    var context = new Mock<HttpContextBase>();
    context.Setup(c => c.Request).Returns(request.Object);
 
    controllerContext = new ControllerContext(context.Object, new RouteData(), controllerFotos);
    controllerFotos.ControllerContext = controllerContext;
 
    //Act
    controllerFotos.Upload();
 
    //Assert
    postedFile.Verify(p => p.SaveAs(It.IsAny<string>()), Times.Once());
}

Explicando o teste

O teste em si é bastante simples.
Nas linhas 6, 7 e 8 o que fazemos é pegar a imagem do arquivo de resources e jogamos para um MemoryStream. Fazemos isso pois é desta forma que o PostedFile trabalha, com um Stream.
ArquivosPostados é o nome do meu arquivo de resource, e ImagemPostada
é o nome da imagem que adicionei ao arquivo.
Nas linhas 10 e 11 criamos um mock para HttpPostedFileBase, que é o tipo a ser retornado pela lista de PostedFiles no nosso controller. E configuramos ele para retornar nossa imagem que está no memory stream.
Nas linhas 13 e 14 configuramos a coleção de arquivos mockando o HttpFileCollectionBase. Dizemos que quando for acessado o índice zero desta coleção deve retornar nosso posted file mockado.
Nas linhas 16 e 17 criamos o mock do HttpRequestBase, desta forma quando for solicitado a coleção de Files retornaremos nossa lista mockada.
Nas linhas 19 e 20 fazemos o mock do HttpContextBase, assim quando solicitarmos o Request retornaremos nosso request mockado.
Para finalizar, nas linhas 22 e 23 criamos um ControllerContext usando nosso HttpContext mockado, e então colocamos este contexto no nosso controller.
Isso tudo serve para que tenhamos controle total sobre todos os arquivos passados para nosso controller, sem haver nenhuma necessidade do upload manual, tornando assim o teste realmente automatizado.
Na linha 26 fazemos a chamada para a action, nesse caso chamada Upload.
Na linha 29 fazemos a verificação para saber se nossa action realmente salvou o arquivo que foi enviado.

Abaixo temos o código do nosso controller:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
public ViewResult Upload()
{
    if (Request.Files[0] != null)
    {
        Request.Files[0].SaveAs(string.Empty);
        return View("UploadSucesso");
    }
 
    return View();
}

Considerações

O código de testes pode parecer longo, e o código do controller relativamente simples. Mas é claro que este código está bem resumido. O controller provavelmente faz mais coisas do que simplesmente salvar a imagem em disco. Talvez hajam validações quanto ao nome, formato, tamanho do arquivo. Talvez exista uma rotina para realizar crop, resize, rename da imagem. Tudo isso deve ser testado, e sinceramente, esse código é bastante simples e rápido de ser escrito, e não custa nada criar alguns testes como este. Sendo assim, acho que há benefício em manter este teste na sua suíte e no seu projeto.

Eu consegui não precisar mockar todos estes objetos utilizando o framework de mock da Telerik – JustMock. Mas como é um produto pago, preferi não basear meu post nele.
Com Rhino e Moq eu não consegui o mesmo resultado. Então se vocês tiverem sugestões e conhecimento de como não mockar todos estes objetos, é só enviar e eu divulgo.

É isso galera, espero que este tipo de testes também lhes seja útil e ajude a criar aplicações ainda mais robustas com ASP.NET MVC.

Abraços,
Vinicius Quaiato.

Testar métodos privados

Filed Under (.NET, TDD) by Vinicius Quaiato on 30-08-2010

Tagged Under : , , , , ,

Não sou fã de testar métodos privados. Na teoria deveríamos testar a interface pública de nossos objetos, porém em alguns casos é necessário testar a interface privada de um objeto.

Em .NET isso está bastante simples coma a utilização da classe PrivateObject.

Vamos ver um exemplo de como isso funciona. Abaixo temos uma classe com 2 métodos e uma propriedade, todos privados:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Privados
{
    private string Nome { get; set; }
 
    private int Soma(int n1, int n2)
    {
        return n1 + n2;
    }
 
    private string HelloPrivateWorld()
    {
        return "Hello private world";
    }
}

E aqui temos os métodos de teste utilizando o PrivateObject:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[TestMethod]
public void Soma_2_e_2_Deve_Retornar_4()
{
    PrivateObject privateObj = new PrivateObject(new Privados());
    var res = privateObj.Invoke("Soma", 2, 2);
 
    Assert.AreEqual(4, res);
}
 
[TestMethod]
public void HelloPrivateWorld_deve_retorar_string_Hello_Private_World()
{
    PrivateObject privateObj = new PrivateObject(new Privados());
    var res = privateObj.Invoke("HelloPrivateWorld");
 
    Assert.AreEqual("Hello private world", res);
}
[TestMethod]
public void Deve_Setar_e_Dar_Get_Em_Pripriedade_Privada()
{
    PrivateObject privateObj = new PrivateObject(new Privados());
    privateObj.SetProperty("Nome", "Vinicius");
 
    Assert.AreEqual("Vinicius", privateObj.GetProperty("Nome"));
}

O código dos testes é bastante simples. Notem que na criação do PrivateObject passo em seu construtor uma instância do objeto que possui os métodos e propriedades privados que eu quero testar.
Depois disso, executo os métodos privados utilizando o método Invoke, conforme mostrado nas linhas 5 e 13.
No caso da propriedade privada eu utilizo o método SetProperty para definir o seu valor, e GetProperty para obter o seu valor.

Simples!

Cuidado!

Nem tudo que é possível é certo de ser feito. É possível roubar um banco, mas nem por isso vamos roubar um banco. Se você tem o código fonte que está sendo testado, e ainda assim tem a necessidade de testar métodos privados, talvez haja um erro de desenho da sua solução e esse código privado deva ser transformado em alguma outra coisa que exponha este conhecimento para o mundo. Não é apenas uma questão de tornar “public”, não estou falando isso. Estou dizendo que talvez deva-se gastar um tempinho compreendendo melhor o cenário, as relações e responsabilidades dos objetos, e talvez extrair algumas classes que estejam “ocultas” pela atual solução.

Abraços,
Vinicius Quaiato.

ASP.NET MVC: Testando Controllers parte II

Filed Under (ASP.NET MVC, Boas Práticas, TDD) by Vinicius Quaiato on 30-08-2010

Tagged Under : , , , , , ,

Continuando a falar sobre testes de controllers no ASP.NET MVC, neste post mostrarei como realizar alguns testes para garantir que as actions retornem as views corretas e com os dados corretos.

A questão de testar os controllers é que, inevitavelmente, eles possuem comportamentos. Suas actions tomam decisões, sabem falar com partes do nosso domínio, e sabem o que retornam para a exibição e de que forma essa exebição será (escolhendo entre as views disponíveis). Desta forma é importante que tenhamos testes que nos auxiliem neste processo, não apenas na “captura” de bugs (pois acho que testes unitários não atuam dessa forma) mas no auxílio a manutenção e evolução do nosso sistema.

Testando uma action de listagem

Este teste é um teste simples que mostra como verificar o comportamento de uma action que deve coordenar a listagem de Produtos.

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[TestMethod]
public void Action_Listar_Retorna_Todos_Produtos_Para_View_ListarProdutos()
{
    var todosProdutos = new List<Produto>
                            {
                                new Produto("Computador"),
                                new Produto("Teclado"),
                                new Produto("Mouse"),
                            };
 
    repositorio
        .Setup(r => r.Todos())
        .Returns(todosProdutos);
 
    var resultView = controller.Listar();
 
    Assert.AreEqual("ListarProdutos", resultView.ViewName);
    Assert.IsTrue(resultView.ViewData.Model is List<Produto>);
    Assert.AreEqual(todosProdutos, resultView.ViewData.Model);
}

Neste teste criamos uma lista de produtos e configuramos o mock do nosso repositório para retornar esta lista.
Na linha 15 fazemos a chamada para a action Listar do nosso controller.
Na linha 17 verificamos se a view retornada é a view ListarProdutos.
Na linha 18 verificamos se o que foi passado para a view é realmente uma List.
E na linha 19 fazemos uma verificação para garantir que os dados passados para a view são os mesmos dados retornados pelo repositório.
Esta última verificação poderia ser diferente, pois poderíamos trabalhar com um ViewModel de produto e não a própria classe, mas isso fica como lição de casa para vocês.

Testando action que retorna 2 views diferentes

Nossa action de listagem pode assumir um outro comportamento. Quando não existe nenhum produto para ser exibido ela deve retornar uma view diferente, uma view de NenhumProduto. Vejamos o teste:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
[TestMethod]
public void Action_Listar_Sem_Nenhum_Produto_Deve_Retornar_View_NenhumProduto()
{
    repositorio
        .Setup(r => r.Todos())
        .Returns(new List<Produto>());
 
    var viewResult = controller.Listar();
 
    Assert.AreEqual("NenhumProduto", viewResult.ViewName);
}

Este é um teste bastante simples também. Configuramos o repositório para retornar uma lista vazia de produtos.
Na linha 8 realizamos a chamada para a action de listagem.
E na linha 10 verificamos que a view retornada deve ser a view NenhumProduto, afinal o repositório não retornou nenhum produto.

Testando action que retorna Json

Agora faremos um pequeno teste em uma action que deve listar os produtos em formato Json. Esta action recebe 2 parâmetros, mais ou menos para realizar uma paginação via ajax.
Vejamos como ficou o teste:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[TestMethod]
public void Action_Obter_Deve_Retornar_Produtos_Em_Formato_Json()
{
    var todosProdutos = new List<Produto>
                    {
                        new Produto("Teclado"),
                        new Produto("Monitor")
                    };
    repositorio
        .Setup(r => r.Todos())
        .Returns(todosProdutos);
 
    var viewResult = controller.Obter(pagina: 1, quantidade: 10);
 
    Assert.IsTrue(viewResult is JsonResult, "Deve ser um JsonResult");
 
    var jsonResult = viewResult as JsonResult;
    var jsonData = jsonResult.Data as List<Produto>;
    Assert.AreEqual(2, jsonData.Count);
}

Neste teste configuro o repositório para retornar uma lista de produtos. Na linha 13 fazemos a chamada para a action informando que queremos 10 resultados da página 1, ou seja, os 10 primeiros resultados vindos do repositório.
Na linha 15 fazemos um assert para verificar que nossa view é na verdade um JsonResult. Fiz isso antes das conversões para obter os dados pois desta forma eu consigo saber se o teste falhará por que não é um JsonResult, caso contrário o cast daria erro e eu ficaria sem uma mensagem clara do que aconteceu.
Por fim, na linha 19 verifico se o total de dados retornados para a view é 2, afinal configurei meu repositório com apenas 2 produtos.

Em resumo…

Desta forma vimos que testar actions que retornam dados, decidem sobre quais views exibir e como os dados devem ser exibidos é relativamente simples. Vimos que uma mesma action que retorna 2 views diferentes está com seu comportamento garantido, e qualquer alteração que for feita será impactada e validada pelos testes que já existem, deixando assim a aplicação mais saudável e simples de evoluir e manter.

Fontes

Os fontes podem ser baixados aqui em Zip, ou aqui no github.

Abraços,
Vinicius Quaiato.

TagCloud em ASP.NET MVC

Filed Under (.NET 4.0, ASP.NET MVC, Projetos) by Vinicius Quaiato on 29-08-2010

Tagged Under : , , , ,

Fala galera, semana passada comecei a desenvolver uma aplicação de TagCloud para exibir “o que estava bombando” no TDC 2010.

Comecei a desenvolver a aplicação no domingo, mas não terminei. Então fiz um “pareamento” com o Giovanni Bassi, e ajudei ele a terminar a aplicação dele. E mais à noite finalizei a minha.

A aplicação está rodando aqui: http://apps.viniciusquaiato.com/tagcloud
O fonte está disponível aqui: http://tdcwords.codeplex.com

A aplicação foi feita usando bibliotecas nativas do .NET 4.0, com ASP.NET MVC 2 e usando testes unitários.

O tempo de desenvolvimento foi de cerca de 1:30h, mas somando o pareamento com o Giovanni diria que foram 2h ou pouco mais que isso, afinal o pareamento agregou conhecimento.

ASP.NET MVC: Testando Controllers parte I

Filed Under (ASP.NET MVC, TDD) by Vinicius Quaiato on 27-08-2010

Tagged Under : , , , , , , ,

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.

?View Code CSHARP
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:

?View Code CSHARP
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.

TechEd Brasil 2010 – Minha agenda

Filed Under (.NET 4.0, Visual Studio 2010) by Vinicius Quaiato on 22-08-2010

Tagged Under : , ,

Fala galera, compartilhando minha agenda (escolhida com muito cuidado por mim e pela primeira dama @devnetgomez):

Programação do dia 13/09/2010
13:45 – 15:00
Título: Desenvolvendo para Azure
Palestrante (s): Otavio Pecego Coelho,

15:30 – 16:45
Título: Arquitetura de Soluções com o Windows Server AppFabric, WCF e WF – Patterns de Aplicações, Serviços e Workflows
Palestrante (s): Waldemir Cambiucci,

17:15 – 18:30
Título: Scrum Process Template para TFS 2010: Seja ágil de verdade! – 150 min
Palestrante (s): André Dias, Giovanni Bassi,

Programação do dia 14/09/2010
09:00 – 10:15
Título: Windows Server AppFabric Caching – construindo aplicações com alto desempenho na plataforma Microsoft
Palestrante (s): Osvaldo Daibert,

10:45 – 12:00
Título: Teste de software com o Visual Studio 2010: Parte 2 de 2
Palestrante (s): Brian Keller, Rodrigo de Carvalho,

13:45 – 15:00
Título: O Projeto Mono: Aplicações .NET para sistemas não-Windows
Palestrante (s): Alessandro Binhara,

15:30 – 16:45
Título: Implementando Serviços RESTful usando o Microsoft .NET Framework
Palestrante (s): Israel Aece,

17:15 – 18:30
Título: Conheça os limites do Windows
Palestrante (s): Guilherme Carnevale,

Programação do dia 15/09/2010
09:00 – 10:15
Título: PowerPivot Avançado: Modelagem, formulas e DAX
Palestrante (s): Thiago Henrique Hernandes Zavaschi,

10:45 – 12:00
Título: Criando Dashboards no PerformacePoint Services do SharePoint 2010
Palestrante (s): Thiago Cruz Soares,

13:45 – 15:00
Título: Criando Rich Internet Applications (RIA) com Silverlight 4 e WCF RIA Services
Palestrante (s): Kelps Leite de Sousa,

15:30 – 16:45
Título: Paralelismo no .Net 4.0: Patterns, dicas e truques
Palestrante (s): Otavio Pecego Coelho,

17:15 – 18:30
Título: Tudo o que você precisa saber sobre Data Mining
Palestrante (s): Roberval Ranches,