Rollback automático com NUnit

A situação é comum: você constrói um conjunto de testes unitários baseado em registros que estão salvos em um banco de dados e tudo funciona bem. O problema é que assim que os dados são alterados/apagados/inseridos pelo primeiro ciclo de testes, seus Asserts simplesmente deixam de funcionar – e você precisa de alguma maneira voltar a base ao seu estado original.

Bom, antes de qualquer coisa vamos deixar algo claro: provavelmente você não devesse estar dependendo de um banco de dados. Se você estiver testando sua camada de domínio, o ideal é que suas suas classes de controle consumam uma interface de suas classes DAO cuja dependência tenha sido injetada. Isto permitirá que você construa stubs de suas classes de acesso a dados, garantindo que seus testes rodarão rapidamente (e que você sempre tenha coragem para rodá-los!) e não dependam de um banco de dados para serem rodados.

Mas suponhamos que você tenha uma razão muito forte para querer depender de um banco de dados. Você pode estar testando a camada de acesso a dados em si, stressar seu database através de testes unitários ou simplesmente já possuir um conjunto muito grande de testes prontos que desencorajam o refactor (não é algo que você deva se orgulhar, mas coisas assim acontecem). Em qualquer um desses casos, você tem algumas opções:

  1. Escrever seus testes para que eles sejam auto-suficientes: uma boa prática, seguindo a “metodologia” AAA de testes unitários: Arrange, Act, Assert. Para alguns casos (com muitos registros envolvidos), entretanto, isto pode ser bastante trabalhoso (e demorado);
  2. Criar um backup da base que você roda os testes e restaurá-lo constantemente: menos trabalhoso, mas longe do ideal. Mesmo automatizando o restore via código trabalho adicional associado sempre que os dados fossem efetivamente alterados (para adição de novos testes, por exemplo);
  3. Recarregar os dados do database a partir de XML, DBF ou algo que o valha: uma opção legal e até bem difundida. Arquivos XML ou de qualquer outro tipo, entretanto, não são minha representação ideal de dados relacionais;
  4. E por último, é claro, a opção que falaremos aqui: utilizar o XtUnit, framework desenvolvido pelo Roy Osherove (uma espécie de bam-bam-bam dos testes unitários) que permite um rollback nos seus dados com um simples attribute de [DataRollBack].

Utilizar o NUnitX é muito fácil. Para utilizá-lo, você só precisa de um teste unitário que faça acesso aos dados e dos passos a seguir:

  1. Baixe o assembly da página do próprio Roy (ou o código se você quiser entender como funciona) e adicione uma referência ao seu projeto de testes;
  2. Adicione um using para o namespace do framework, pra facilitar a utilização dos attributes:

    10 using TeamAgile.ApplicationBlocks.Interception.UnitTestExtensions;

  3. Herde sua classe de testes de ExtensibleFixture:

    14 [TestFixture]

    15 public class NullableTests : ExtensibleFixture

  4. Coloque o attribute de DataRollBack em um de seus métodos que fazem acesso a dados:

    71 [Test]

    72 [DataRollBack]

    73 public void PersistNullablePersonWithNullValue()

    74 {

    75 // acesso a dados e tudo mais

    76 }

E pronto! Basta rodar seus testes e ver que seus dados permanecem inalterados mesmo após todos aqueles INSERTs, UPDATEs e DELETEs. Mas como isso acontece? Por trás dos panos o XtUnit utiliza técnicas de orientação a aspectos pra interceptar o início e o fim de cada método de teste, criando uma transação distribuída (garanta que seu MSDTC esteja ativado e corretamente configurado!) e fazendo o rollback no final. Como a transação é distribuída, mesmo que você envolva diferentes bancos de dados tudo vai ser desfeito no final do teste.

É claro que tanto poder traz alguns efeitos colaterais. Diria que “leve” não é a palavra mais apropriada para definir a maneira como a inalteração dos dados é controlada. Mas a praticidade certamente compensa para um conjunto limitado de testes. Por isso, use com moderação!

Abraços e até a próxima!

José Filipe

Anúncios

2 Responses to “Rollback automático com NUnit”


  1. 1 Marcos 15/06/2009 às 09:45

    Aqui na minha empresa usamos os testes unitários sempre com uma base de dados vazia, e a cada novo teste nós limpamos a base, e inserimos os dados necessários para que o teste rode.

    Não entendi quando você diz
    “o ideal é que suas suas classes de controle consumam uma interface de suas classes DAO cuja dependência tenha sido injetada.”, poderia dar um exemplo?

    Muito bom artigo…

    []s

    • 2 José Filipe 18/06/2009 às 20:53

      Marcos, um exemplo simples seria assim: imagina que tens uma classe de controle “UserController” que cria internamente um “UserDAO”, que por sua vez acessa um SGBD por trás. Pra fazeres os testes unitários no teu Controller, obrigatoriamente tua base tem que estar funcionando, limpa, etc, correto?

      Pois bem, se tua classe DAO implementasse um IUserDAO e teu Controller, ao invés de criar essa classe internamente, recebesse no seu construtor um IUserDAO, pros teus testes tu poderia criar uma classe UserDAOStub (uma classe fake) que implementaria todos os métodos usando um List por trás por exemplo, e pronto, tu nunca mais dependeria do teu banco pra testar a lógica do teu Controller, concorda?

      Essa prática, além de facilitar o teste, também garante que teus testes serão muito performáticos (o que te garante que não vais gastar tempo pra rodá-los, então vais rodar sempre, e aí por diante).

      Vou ver se meu próximo post (tirando o de hoje! :)) sai sobre injeção de dependência. Tenho que escolher um container pra usar de exemplo, mas a lógica de todos é muito semelhante.

      Abraços!


Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s




Perfil

Olá! Meu nome é José Filipe e sou o autor deste blog. Trabalho como Gerente de Desenvolvimento da todo! BPO, onde sou responsável pela manutenção e evolução de pessoas, processos e sistemas desenvolvidos em diferentes tecnologias. Atualmente curso uma especialização em Engenharia de Software pela PUCPR, em Curitiba, mas moro em Florianópolis, onde me graduei em Sistemas de Informação pela UFSC. Possuo o título de MCP e com base nas experiências do dia-a-dia espero trazer ao blog assuntos interessantes sobre arquitetura e desenvolvimento .NET..

Arquivos

Páginas

junho 2009
S T Q Q S S D
« maio    
1234567
891011121314
15161718192021
22232425262728
2930  

%d blogueiros gostam disto: