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.

2 Respostas to “Criando stub’s com Moq e NBuilder para testes de unidade”

  1. flaviohenriquedecarvalho 13/05/2012 às 14:09 #

    Gostei do seu post, muito didático.

    • andreborges 20/05/2012 às 20:53 #

      Valeu Flávio,

      Estou me preparando para “reativar” o blog, em breve vou postar mais sobre esse assunto.

      Abraços

Deixe um comentário