Arquivo | Testes de unidade RSS feed for this section

Testando mensagens em Exceptions com MSTest

18 ago

Pessoal,

De volta ao blog, finalmente.

Todos devem saber que nos testes de unidade, quando queremos verificar se uma determinada exceção foi disparada no método que está sendo testado, se estivermos utilizando o MSTest (ferramenta de testes nativa do Visual Studio) utilizamos o atributo [ExpectedException]. Mais ou menos assim (não reparem no teste, trata-se de um exemplo):

[TestMethod]
[ExpectedException(typeof(MyBusinessException))]
public void Salvar_ComEmailDuplicado_LevantaExcecao()
{
   // Arrange
   var umaClasseDeNegocio = new ClasseDeNegocio();
   var umaEntidade = new UmaEntidade
   {
      Nome = "Nome válido",
      Email = "email@existente.com",
      Endereco = "Endereco com tamanho valido",
      Quantidade = 5
   };

   // Act
   umaClasseDeNegocio.Salvar(umaEntidade);
}

Porém, várias pessoas perguntam como fazer para, além de verificar se a exceção foi disparada, testar também a mensagem da exceção. Normalmente antes de responder, pergunto: Tem certeza que precisa testar isso?

Pergunto isso porque ficar testando pela mensagem da exceção normalmente me parece um code smell, pois em boa parte dos casos isso pode significar que poderíamos estar fazendo um tratamento de exceção mais específico, ao invés de ficar levantando exceções “genéricas” com mensagens diferentes. Mas, isso é um outro assunto – Vamos admitir que nesse caso o tratamento de exceções esteja adequado.

Infelizmente o MSTest não nos disponibiliza um atributo que permita verificar a mensagem da exceção (O NUnit disponibiliza). Assim, uma forma de fazer essa verificação é dessa forma (mais uma vez, trata-se de um exemplo):

[TestMethod]
public void Salvar_ComEmailDuplicado_LevantaExcecao()
{
   // Arrange
   var umaClasseDeNegocio = new ClasseDeNegocio();
   var umaEntidade = new UmaEntidade
   {
      Nome = "Nome válido",
      Email = "email@existente.com",
      Endereco = "Endereco com tamanho valido",
      Quantidade = 5
   };

   // Act
   try
   {
      umaClasseDeNegocio.Salvar(umaEntidade);

      // Assert
      Assert.Fail("Nenhuma exceção lançada");
   }
   catch (MyBusinessException ex)
   {
      // Assert
      Assert.AreEqual("Este e-mail já está cadastrado", ex.Message);
   }
}

Horrível. Uma outra forma de fazer, e é o que eu faço, é criar um atributo herdando da classe ExpectedExceptionBaseAttribute (o atributo [ExpectedException] também herda dela) e verificar o tipo da exceção e da mensagem. Assim:

public class ExpectedExceptionMessageAttribute
                                         : ExpectedExceptionBaseAttribute
{
   protected Type ExceptionType { get; set; }
   protected string ExceptionMessage { get; set; }

   public ExpectedExceptionMessageAttribute(Type exceptionType,
                                            string exceptionMessage)
   {
      this.ExceptionType = exceptionType;
      this.ExceptionMessage = exceptionMessage;
   }

   public ExpectedExceptionMessageAttribute(Type exceptionType, 
                                            string exceptionMessage,
                                            string noExceptionMessage)
   : base(noExceptionMessage)
   {
      this.ExceptionType = exceptionType;
      this.ExceptionMessage = exceptionMessage;
   }

   protected override void Verify(Exception exception)
   {
      if (exception.GetType() != this.ExceptionType)
      {
         var errorMessage
                 = string.Format("Test method {0} did not throw expected
                                  exception {1}.",
                                  this.TestContext.TestName,
                                  this.ExceptionType.FullName);
         throw new AssertFailedException(errorMessage);
      }

      if (exception.Message != this.ExceptionMessage)
      {
         var errorMessage
                 = string.Format("Test method {0} did not throw expected
                                  exception with message '{1}'.
                                  The message was '{2}'.", 
                                  this.TestContext.TestName,
                                  this.ExceptionMessage,
                                  exception.Message);
         throw new AssertFailedException(errorMessage);
      }
   }
}

Assim, aquele teste que verifica a mensagem ficaria assim:

[TestMethod]
[ExpectedExceptionMessage(typeof(MyBusinessException), "Este e-mail já está cadastrado")]
public void Salvar_ComEmailDuplicado_LevantaExcecao()
{
   // Arrange
   var umaClasseDeNegocio = new ClasseDeNegocio();
   var umaEntidade = new UmaEntidade
   {
      Nome = "Nome válido",
      Email = "email@existente.com",
      Endereco = "Endereco com tamanho valido",
      Quantidade = 5
   };

   // Act
   umaClasseDeNegocio.Salvar(umaEntidade);
}

Bem melhor, IMHO.

Forte abraço.

IslandUnit, uma ferramenta que facilita isolamento em testes de unidade

11 mar

Pessoal,

Tenho falado bastante aqui sobre testes de unidade na plataforma .Net. Falei sobre injeção de dependência (Veja: Parte 1 e Parte 2), sobre o NBuilder e sobre como usar o Moq e o NBuilder em nossos testes de unidade.

Foi liberada uma versão beta do IslandUnit, uma ferramenta Open Source que auxilia na utilização dessas várias ferramentas que usamos para fazer isolamento nos testes de unidade.

Com uma interface fluente bem simples de usar, facilita a utilização de frameworks consagrados no mercado, como Moq, Ninject, NBuilder, entre outros.

Vale a pena dar uma olhada! Veja com é simples de usar no Quick Start Guide e depois veja funcionando em um exemplo.

Ah! Ia me esquecendo… O IslandUnit já está disponível no Nuget.

Além de absolutamente indispensável, escrever testes de unidade está ficando cada vez mais divertido! Logo, logo vou fazer um post demonstrando o IslandUnit junto com o Sharp Tests Ex, que também é muito legal.

Forte abraço.

Criando stub’s com Moq e NBuilder para testes de unidade

14 fev

Pessoal,

Recentemente falei sobre injeção de dependência (Veja: Parte 1 e Parte 2), de modo a minimizarmos o acoplamento nas nossas aplicações, onde usamos o Ninject como container de Inversão de Controle (IoC – Inversion of Control) e aproveitamos para apresentar o NuGet.

Também falei recentemente sobre o NBuilder, que nos ajuda a criar objetos fake, para usarmos nos nossos testes de unidade.

A idéia agora é mostrar como criar stub’s para utilizarmos nos nossos testes de unidade. Para isso, usamos algum framework de isolamento (Moq, NMock, Rhino Mocks, TypeMock, etc…). Para quem não sabe o que é um stub, ou acha que stub e mock é tudo a mesma coisa, recomendo fortemente a leitura do artigo: Mocks não são Stubs, do Martin Fowler.

Mas porque precisamos de um stub?
Dizemos que um teste não é um teste de unidade se:

  • Ele se conecta a um banco de dados;
  • Ele se comunica pela rede;
  • Ele “toca” o sistema de arquivos;
  • Ele não pode rodar junto com algum outro dos seus testes de unidade.

Assim, na prática, usamos mock‘s e stub‘s para “simular” acesso a bancos de dados, comunicação via rede, acesso ao sistemas arquivos, etc… Com isso, dizemos que estamos isolando o sistema que está sendo testado.

Vamos ao código?
Como sempre, vamos direto ao assunto, normalmente é mais fácil de entender a idéia. Vamos utilizar o mesmo exemplo dos post’s citados acima:

Interface:

public Interface ICategoryRepository
{
   IList GetAll();
}

Repositório “verdadeiro”:

public class CategoryRepository : ICategoryRepository
{
   public IList GetAll()
   {
      DataContext ctx = new DataContext();
      return ctx.Categories;
   }
}

Repositório “fake”:

public class CategoryRepositoryFake : ICategoryRepository
{
   public IList GetAll()
   {
      var category1 = new Category() { Id = 1, Name = "Cat 1" };
      var category2 = new Category() { Id = 2, Name = "Cat 2" };
      var category3 = new Category() { Id = 3, Name = "Cat 3" };

      return new List() { category1, category2, category3 };
   }
}

Controller:

public class CategoryController : Controller
{
   [Inject]
   public ICategoryRepository Repository { get; set; }

   public ActionResult Index()
   {
      var categories = this.Repository.GetAll();

      return View(categories);
   }
}

A nossa motivação agora é eliminar a classe “CategoryRepositoryFake”. Para isso, utilizaremos o Moq que, dada uma interface (no nosso caso “ICategoryRepository”), ele nos “entrega” um objeto fake automaticamente. Na verdade, ele cria um objeto proxy, cuja finalidade é a de “fingir” ser um objeto “real”, para que um outro componente (ou objeto) possa “acreditar” que está falando com um objeto “real”. Basicamente, a mesma coisa que fizemos quando criamos manualmente o objeto fake… Só que o Moq faz isso de forma transparente e automática, e de quebra nos traz uma série de outros benefícios (coisa pra outro post, em breve).

Vamos ver como ficaria um teste de unidade da action “Index”, usando o Moq, NBuilder e o Ninject. Toda a “brincadeira” é feita na seção “Arrange” do teste:

[TestClass()]
public class CategoryControllerTest
{
   private IKernel kernel = new StandardKernel();

   [TestMethod()]
   public void Index_Returns_List_Of_Categories()
   {
      // Arrange
      // Cria o mock usando o Moq
      Mock<ICategoryRepository> categoryRepositoryMock
                                        = new Mock<ICategoryRepository>();

      // Cria dados "fake" com o NBuilder
      IEnumerable<Category> categoriesList
                         = Builder<Category>.CreateListOfSize(10).Build();

      // Faz o setup do mock usad pelo SUT, para retornar os dados
      // criados com o NBuilder. Acabamos de criar um STUB.
      categoryRepositoryMock
                          .Setup(m => m.GetAll()).Returns(categoriesList);

      // Informamos ao Ninject: Quando alguém precisar de um
      // ICategoryRepository, entregue o objeto criado pelo mock
      kernel.Bind<ICategoryRepository>()
                            .ToMethod(m => categoryRepositoryMock.Object);

      // Act
      CategoryController sut = kernel.Get<CategoryController>();
      ViewResult viewResult = sut.Index();

      // Assert
      IEnumerable<Category> viewData
                     = viewResult.ViewData.Model as IEnumerable<Category>;

      Assert.IsNotNull(viewData);
      Assert.AreEqual(10, viewData.Count());
   }
}

Algumas observações:
Nas linhas 11 e 12, estamos efetivamente criando o nosso objeto mock, baseado na interface “ICategoryRepository”.

Nas linhas 20 e 21, estamos configurando o mock para que, quando alguém invocar o método “GetAll()” do mock, “forçamos” o retorno de uma lista de objetos criada anteriormente, em memória. Acabamos de definir o “tal” stub.

Outro ponto importante está na linha 26. Repare que estamos adicionando ao Ninject uma referência para a interface “ICategoryRepository” apontando para a propriedade “Object” do nosso mock. A propriedade “Object” nos retorna o objeto proxy que comentei anteriormente, e que implementa a interface “ICategoryRepository”.

O que acharam? Nesse post, vimos como criar stub‘s. No próximo, vamos criar mock‘s.

Forte abraço.

Configurando DI em aplicações ASP.NET MVC 3 com Ninject usando NuGet – Parte 1

8 dez

Pessoal,

Esse é a primeira parte (de dois) desse post. Nessa parte, tentarei falar de forma mais simples e direta possível sobre o pattern injeção de dependência (DI – Dependency Injection). Na segunda parte, mostrarei como configurar e utilizar um container de IoC (Inversion of Control) em uma aplicação ASP.NET MVC 3, utilizando o NuGet.

Para quem não conhece DI, não vou entrar em detalhes conceituais aqui, pois existe muita referência na internet, vou somente recomendar algumas:

Inversion of Control Containers and the Dependency Injection pattern (Martin Fowler)
Wikipedia
Design Patterns: Dependency Injection (MSDN)
Dependency Injection for Loose Coupling

Sendo assim, vamos ver exemplos práticos. Supondo que temos o seguinte código:

Repositório:

public class CategoryRepository
{
   public IList<Category> GetAll()
   {
      DataContext ctx = new DataContext();
      return ctx.Categories;
   }
}

Controller:

public class StoreController : Controller
{
   public ActionResult Index()
   {
      CategoryRepository categoryRepository = new CategoryRepository();
      IList<Category> categories = categoryRepository.GetAll();

      return View(categories);
   }
}

Veja que na linha 5 nós estamos instanciando um CategoryRepository, que acaba nos deixando com um forte acoplamento entre o Controller e o CategoryRepository.

Por isso que, se formos implementar os testes de unidade deste Controller, precisamos criar um Stub desse repositório (uma espécie de repositório “fake”), para que os nossos testes de unidade não precisem se conectar ao banco de dados efetivamente. Assim, quando estivermos executando o teste de unidade desse Controller, vamos precisar “trocar” o repositório CategoryRepository por um repositório que nos retorne um conjunto de dados “fake”, como por exemplo um CategoryRepositoryMock.

Para fazer essa “troca” de repositórios, em primeiro lugar criamos uma interface para o nosso repositório, de modo que tanto o repositório “verdadeiro” quanto o repositório “fake” implementem essa interface, e o nosso Controller terá uma referência para essa interface, e não para os repositórios em si:

Interface:

public Interface ICategoryRepository
{
   IList<Category> GetAll();
}

Repositório “verdadeiro”:

public class CategoryRepository : ICategoryRepository
{
   public IList<Category> GetAll()
   {
      DataContext ctx = new DataContext();
      return ctx.Categories;
   }
}

Repositório “fake”:

public class CategoryRepositoryFake : ICategoryRepository
{
   public IList GetAll()
   {
      Category category1 = new Category() { Id = 1, Name = "Cat 1" };
      Category category2 = new Category() { Id = 2, Name = "Cat 2" };
      Category category3 = new Category() { Id = 3, Name = "Cat 3" };

      return new List<Category>() { category1, category2, category3 };
   }
}

Controller:

public class StoreController : Controller
{
   ICategoryRepository categoryRepository;

   public StoreController() : this(new CategoryRepository())
   {
   }

   public StoreController(ICategoryRepository repository)
   {
      this.categoryRepository = repository;
   }

   public ActionResult Index()
   {
      IList<Category> categories = this.categoryRepository.GetAll();

      return View(categories);
   }
}

Veja que criamos no Controller dois construtores. O primeiro deles não recebe nenhum parâmetro, enquanto o outro recebe por parâmetro um objeto que implemente a interface ICategoryRepository, que executa o outro construtor passando o repositório “verdadeiro”. É esse construtor sem parâmetros que é chamado pelo framework ASP.NET MVC ao instanciar o Controller. Nos nossos testes de unidade, usaremos o segundo construtor para que possamos passar o repositório “fake” para o Controller:

Teste de unidade:

public class StoreControllerTests
{
   [TestMethod]   
   public void Index_Test()
   {
      // Arrange
      ICategoryRepository categoryRepository
                                            = new CategoryRepositoryFake();
      StoreController controller = new StoreController(categoryRepository);

      // Act
      ViewResult result = controller.Index() as ViewResult;

      // Assert
      Assert.IsNotNull(result);
      Assert.AreEqual("Index", result.ViewName);
   }
}

Veja bem: Nós já implementamos uma forma do pattern injeção de dependência (Constructor Injection). O nosso repositório não é mais instanciado dentro do Controller, mas é “injetado” através do construtor (linha 8). Daí vem a “tal” Inversion of Control, pois a responsabilidade de instanciar o repositório não é mais do Controller, mas de alguma outra classe. Em outras palavras, quem controla qual “versão” do repositório será utilizada pelo Controller não é mais o próprio Controller.

Isso funciona, mas temos alguns problemas. Imagine a situação de utilizarmos vários repositórios no nosso Controller… Teríamos que ter vários parâmetros no nosso construtor, além de termos uma dependência no mínimo “incômoda” do construtor sem parâmetros com a implementação do repositório “verdadeiro”.

O que fazer então? É aí que entram em cena os container‘s de IoC. Mas esse é exatamente o assunto principal desse post, e vai ser tratado na segunda parte, onde iremos configurar o container de IoC Ninject utilizando o NuGet usando esse mesmo exemplo.

Forte abraço.

Usando o NBuilder para criação de objetos “fake”

1 dez

Pessoal,

Recentemente descobri (não sei se vocês já conheciam) uma ferramenta bem interessante, que tem ajudado bastante na criação de stub‘s para os testes de unidade. Trata-se do NBuilder (http://nbuilder.org).

O NBuilder é um projeto open source, que permite criar objetos a partir das suas classes muito rapidamente, através de uma interface fluente e extensível. Ele instancia os objetos e atribui automaticamente valores às propriedades e campos públicos da sua classe, além de permitir que você substitua os valores das propriedades dos objetos criados usando expressões lambda.

Bom, como sempre, vamos aos exemplos práticos que sempre são mais elucidativos que a teoria. Acredito que rapidamente vocês vão entender a “mão na roda” que essa ferramenta é. Para que você possa reproduzir esses exemplos, basta fazer o download do NBuilder e referenciá-lo no seu projeto.

Tomando como exemplo esse domínio:

public class Blog : IBusinessEntity
{
   public int Id { get; set; }
   public string Name { get; set; }
   public IList Posts { get; set; }
}

public class Post : IBusinessEntity
{
   public int Id { get; set; }
   public string Title { get; set; }
   public string Text { get; set; }
   public string Author { get; set; }
}

Criei alguns testes de unidade para exemplificar a utilização do NBuilder:

[TestMethod]
public void Build_Blog()
{
	// Arrange

	// Act
	var blog = Builder<Blog>.CreateNew().Build();

	// Assert
	Assert.IsNotNull(blog);
	Assert.AreEqual(1, blog.Id);
	Assert.AreEqual("Name1", blog.Name);
}

Perceberam o que o método Builder<Blog>.CreateNew() fez? Ele instanciou um objeto do tipo Blog e preencheu as propriedades Id e Name apropriadamente, e o método Build() nos retornou o objeto. Simples demais.

Podemos também criar listas de objetos muito facilmente:

[TestMethod]
public void Build_ListOfPosts()
{
	// Arrange

	// Act
	var posts = Builder<Post>.CreateListOfSize(10).Build();

	// Assert
	Assert.AreEqual(10, posts.Count);
	Assert.AreEqual(1, posts.First().Id);
	Assert.AreEqual(10, posts.Last().Id);
	Assert.AreEqual("Title1", posts.First().Title);
	Assert.AreEqual("Title10", posts.Last().Title);
	Assert.AreEqual("Text1", posts.First().Text);
	Assert.AreEqual("Text10", posts.Last().Text);
	Assert.AreEqual("Author1", posts.First().Author);
	Assert.AreEqual("Author10", posts.Last().Author);
}

Muito legal. O método Builder<Post>.CreateListOfSize(int) instanciou 10 objetos do tipo Post, preencheu as propriedades apropriadamente e os retornou em um IList<Post>:

  • A propriedade Id (do tipo int), recebeu os valores 1, 2, …, 10;
  • A propriedade Title (do tipo string), recebeu os valores “Title1”, “Title2”, …, “Title10”;
  • A propriedade Author (do tipo string), recebeu os valores “Author1”, “Author2”, …, “Author10”.
  • Etc…

No primeiro exemplo, a propriedade Posts (que é um IList<Post>) do objeto Blog não foi preenchida. Para que essa propriedade seja preenchida, precisamos usar o método With:

[TestMethod]
public void Build_ListOfBlogsWithPosts()
{
	// Arrange
	var posts = Builder<Post>.CreateListOfSize(10).Build();

	// Act
	var blog = Builder<Blog>.CreateNew()
                       .With(b => b.Posts = posts).Build();

	// Assert
	Assert.IsNotNull(blog);
	Assert.AreEqual(10, blog.Posts.Count);
	Assert.AreEqual(1, blog.Posts.First().Id);
	Assert.AreEqual(10, blog.Posts.Last().Id);
	Assert.AreEqual("Title1", blog.Posts.First().Title);
	Assert.AreEqual("Title10", blog.Posts.Last().Title);
}

Esses são exemplos bem simples, mas nos dá uma idéia do que podemos fazer com a ferramenta. Temos vários outros métodos, que nos permite:

Bem, isso foi só uma amostra do que é possível fazer com o NBuilder, vale muito a pena dar uma olhada.

Em um próximo post vou mostrar algumas “coisas” um pouco mais complexas que já fiz com ele, além de mostrar como o tenho utilizado juntamente com o Moq (http://code.google.com/p/moq), para criação de stub‘s nos testes de unidade.

Forte abraço.