Arquivo

Archive for abril \28\UTC 2012

Como testar e gerar mock de membros internal e protected

O Rhino Mocks(http://www.ayende.com/projects/rhino-mocks.aspx) é um framework para geração de mocks, artifício vastamente utilizado na abordagem de desenvolvimento TDD. Um grande problema é que o Rhino Mocks ao contrário de frameworks como Moq(http://code.google.com/p/moq/), não conseguem “mockear”  metodos e propriedades com modificadores de acesso restrito como protected e internal, isso força o desenvolvedor a definir como public todos os  membros implementados, isso acarreta em uma exposição indesejada(mas forçada) do que não é verdadeiramente público.

E ai o que fazer?

Existem alguns caminhos a se seguir  :  mudar de framework , implementar duas interfaces  para cada classe( uma com a exposição do que realmente se deve expor para quem irá utilizar a classe e outra com todos os metodos e propriedades expostos, para serem utilizados pelos casos de teste), entre outras formas possíveis, existem duas que irei apresentar neste post : Mock de modificadores internal e Protected;

Algumas definições

internal 

 O tipo ou membro internal pode ser acessado por qualquer código implementado no mesmo assembly.

protected

O tipo ou membro protected pode ser acessado somente dentro da classe que ele for implementado ou em classes que                        especialize a classe que tem o mesmo implementado.

Modificador  de acesso : internal

Para exemplificar o mock destes dois tipos de modificadores de uma forma mais didatica, eu criei um cenário bem simples(bem simples mesmo), vejamos na pratica.

Criei um projeto “Dominio”

Implementei duas classes :

Pedido

public class Pedido
{
   public virtual List<Item&gt; itens{get;set;}
   public virtual decimal ValorTotal { get; set; }

   public Pedido()
   {
      this.itens = new List<Item>();
   }

   public virtual void  AdicionarItem(Item item)
   {
      CalcularTotalDoPedido(item);
      this.itens.Add(item);
   }

   Internal virtual void CalcularTotalDoPedido(Item item)
   {
      throw new NotImplementedException()
   }
}

Itens

public class Item
{
    public virtual Pedido Pedido { get; set; }

    public Item()
    {

    }

}

O código visa  incluir um item no pedido,  faz parte do processo de incluir item um  calculo  para atualizar o total do pedido.Perceba que fora implementado o metodo AdicionarItem e este é público já o metodo CalcularTotalDoPedido é internal e está NotImplemented.

Criei um projeto de Testes Dominio.Testes

Criei uma classe de testes, PedidoTestes.


Note que não tenho acesso ao método internal CalcularTotalDoPedido.Normalmente os casos de Teste estão implementados em um outro assembly, sendo assim  não consigo gerar o mock de alguem que seja internals, porem existe uma brexa para se fazer isso sem muitos esforços.

Preciso informar ao assembly que todos os internals serão visiveis para um assembly especifico. Existe um arquivo dentro de Properties do projeto, chamado AssemblyInfo.cs, conforme figura

Inclua no final do arquivo o seguinte trecho de código :

//Para visibilidade dentro do assembly de testes
//Perceba que o assembly é o assembly de testes
[assembly: InternalsVisibleTo("Dominio.Testes")]

Se acessarmos o caso de teste implementado veremos que agora nós temos acesso ao método internal CalcularTotalDoPedido, e sendo assim, podemos fazer o mock do mesmo.

Agora conseguimos mockear e até testar se for o caso um metodo  de modificador internal.

Modificador  de acesso : protected

Antes de mais nada preciso explicar o que percebi e que me possibilita mockear ou até mesmo testar algo protegido.

Sabemos por definição que :

O tipo ou membro protected pode ser acessado somente dentro da classe que ele for implementado ou em classes que especializão a classe que tem o mesmo implementado.

Logo :

Se “Classe B” Especializa “Classe A” então “Classe B” tem acesso na sua implementação a  membros da “Classe A”.

O Natural é que ambas as instancias de B e A não exponham o método protegido, como vemos a seguir :

No entanto se a “Classe B” instancia ela mesma então temos acesso a todo conteudo protected da “Clase A” na instância da “Classe B”, ficando assim :

Caso a classe C  especialize a classe A

Obteremos o mesmo resultado quando tentamos instanciar a “Classe B” e acessar metodos protected :

Mas se tentarmos instanciar  a “Classe C”  nela mesma teremos :

Observando isso conclui-se  que:

Dado uma classe X  que tenha  membros protected e instancie a si mesma em um objetox, o objetox irá expor todos os  membros. Isso nos possibilita gerar mock e testar.

Vamos aproveitar o mesmo cenário porem trabalhando com o modificador protected :

O metodo da classe Pedido CalcularTotalDoPedido agora será protected, agora piorou a história pois preciso fazer um mock dele e não tenho acesso a ele.

Vamos recordar como funciona o modificador protected :

Visto que uma classe especialista( Herança) da classe Pedido tem acesso aos  metodos protected,  temos um caminho para fazermos o teste de Pedidos.

A classe PedidoTestes Irá especializar Pedido, ok e dai??

Perceba que isso me possibilita testar a classe Pedidos  indiretamente. O código vai ficar assim :

Veja que ao invez de mockear pedido tendo ele como alvo direto, eu faço o mock de PedidoTestes.

var pedido = repositorioMock.PartialMock<PedidoTestes>();

E em seguida  eu gero uma espectativa para  CalcularTotalDoPedido que é protegida na implementação real

pedido.Expect(p => p.CalcularTotalDoPedido(null)).IgnoreArguments();

Rodando o teste :

Acessando um serviço que consumiria a classe Pedido vemos que o método Calcular

CalcularTotalDoPedido não é visível.

Vimos a possibilidade de gerar mock de membros sejam eles internal ou protected,somente com os recursos de Orientação a Objetos e do .Net.Sendo assim você pode continuar usando o framework Rhino Mocks ou qualquer outro que você já usa sem poluir sua modelagem com um emaranhado de coisas publicas sem a real necessidade.

Até mais!

Categorias:Boas Praticas Tags: