Pergunta

No verão passado, eu estava desenvolvendo um aplicativo CRUD ASP.NET/SQL Server básico, e o teste de unidade era um dos requisitos.Tive alguns problemas quando tentei testar o banco de dados.No meu entender, os testes unitários devem ser:

  • apátrida
  • independentes um do outro
  • repetível com os mesmos resultados, ou seja,sem alterações persistentes

Esses requisitos parecem estar em desacordo durante o desenvolvimento de um banco de dados.Por exemplo, não posso testar Insert() sem ter certeza de que as linhas a serem inseridas ainda não existem, portanto, preciso chamar Delete() primeiro.Mas, e se eles ainda não estiverem lá?Então eu precisaria chamar a função Exists() primeiro.

Minha solução final envolveu funções de configuração muito grandes (eca!) e um caso de teste vazio que seria executado primeiro e indicaria que a configuração foi executada sem problemas.Isto significa sacrificar a independência dos testes, mantendo ao mesmo tempo a sua apatridia.

Outra solução que encontrei é agrupar as chamadas de função em uma transação que pode ser facilmente revertida, como XtUnit de Roy Osherove.Este trabalho, mas envolve outra biblioteca, outra dependência, e parece uma solução um pouco pesada para o problema em questão.

Então, o que a comunidade SO fez quando confrontada com esta situação?


tgmdbm disse:

Você normalmente usa sua estrutura de teste de unidade automatizada favorita para realizar testes de integração, e é por isso que algumas pessoas ficam confusas, mas não seguem as mesmas regras.Você pode envolver a implementação concreta de muitas de suas classes (porque elas foram testadas na unidade).Você está testando Como suas classes de concreto interagem entre si e com o banco de dados.

Então, se eu li isso corretamente, não há realmente nenhuma maneira de efetivamente teste de unidade de uma camada de acesso a dados.Ou um "teste de unidade" de uma camada de acesso a dados envolveria testar, digamos, o SQL/comandos gerados pelas classes, independentemente da interação real com o banco de dados?

Foi útil?

Solução

Não há outra maneira real de testar a unidade de um banco de dados além de afirmar que as tabelas existem, contêm as colunas esperadas e têm as restrições apropriadas.Mas geralmente não vale a pena fazer isso.

Você normalmente não unidade testar o banco de dados.Você geralmente envolve o banco de dados em integração testes.

Normalmente você usa sua estrutura de teste de unidade automatizada favorita para realizar testes de integração, e é por isso que algumas pessoas ficam confusas, mas não seguem as mesmas regras.Você tem permissão para envolver a implementação concreta de muitas de suas classes (porque elas foram testadas em unidade).Você está testando como suas classes concretas interagem entre si e com o banco de dados.

Outras dicas

Unidade DB

Você pode usar esta ferramenta para exportar o estado de um banco de dados em um determinado momento e, então, quando estiver testando a unidade, ele poderá ser revertido automaticamente para seu estado anterior no início dos testes.Nós o usamos com bastante frequência onde trabalho.

A solução usual para dependências externas em testes unitários é usar objetos simulados - ou seja, bibliotecas que imitam o comportamento dos reais contra os quais você está testando.Isso nem sempre é simples e às vezes requer alguma engenhosidade, mas existem várias bibliotecas simuladas boas (freeware) para .Net se você não quiser "criar o seu próprio".Dois vêm à mente imediatamente:

Simulações de rinoceronte é aquele que tem uma reputação muito boa.

NMock é outro.

Existem muitas bibliotecas simuladas comerciais disponíveis também.Parte de escrever bons testes de unidade é, na verdade, projetar seu código para eles - por exemplo, usando interfaces onde faz sentido, para que você possa "simular" um objeto dependente implementando uma versão "falsa" de sua interface que, no entanto, se comporta de maneira semelhante. maneira previsível, para fins de teste.

Em simulações de banco de dados, isso significa "zombar" de sua própria camada de acesso ao banco de dados com objetos que retornam objetos de tabela, linha ou conjunto de dados compostos para seus testes de unidade lidarem.

Onde eu trabalho, normalmente criamos nossas próprias bibliotecas simuladas do zero, mas isso não significa que você precise fazer isso.

Sim, você deve refatorar seu código para acessar repositórios e serviços que acessam o banco de dados e você pode então simular ou fazer stub desses objetos para que o objeto em teste nunca toque no banco de dados.Isto é muito mais rápido do que armazenar o estado do banco de dados e redefini-lo após cada teste!

Eu recomendo Quantidade mínima como sua estrutura de zombaria.Eu usei Rhino Mocks e NMock.O Moq foi muito simples e resolveu todos os problemas que tive com os outros frameworks.

Eu tive a mesma pergunta e cheguei às mesmas conclusões básicas que os outros respondentes aqui:Não se preocupe em testar a unidade da camada de comunicação do banco de dados real, mas se você quiser testar a unidade das funções do seu modelo (para garantir que eles estão extraindo dados corretamente, formatando-os corretamente, etc.), use algum tipo de fonte de dados fictícia e testes de configuração para verificar os dados que estão sendo recuperados.

Eu também acho que a definição básica de teste de unidade não se adapta a muitas atividades de desenvolvimento web.Mas esta página descreve alguns modelos de testes unitários mais ‘avançados’ e pode ajudar a inspirar algumas ideias para aplicar testes unitários em diversas situações:

Padrões de teste unitário

Expliquei uma técnica que tenho usado para esta mesma situação aqui.

A ideia básica é exercitar cada método em seu DAL - afirmar seus resultados - e quando cada teste for concluído, reverter para que seu banco de dados fique limpo (sem dados indesejados/de teste).

O único problema que você pode não achar "ótimo" é que normalmente faço um teste CRUD inteiro (não puro da perspectiva do teste de unidade), mas esse teste de integração permite que você veja seu código de mapeamento CRUD + em ação.Dessa forma, se ele quebrar, você saberá antes de iniciar o aplicativo (me poupa muito trabalho quando estou tentando ir rápido)

O que você deve fazer é executar seus testes a partir de uma cópia em branco do banco de dados gerado a partir de um script.Você pode executar seus testes e, em seguida, analisar os dados para ter certeza de que eles contêm exatamente o que deveriam após a execução dos testes.Aí é só deletar o banco de dados, já que é descartável.Tudo isso pode ser automatizado e pode ser considerado uma ação atômica.

Testar a camada de dados e o banco de dados juntos deixa poucas surpresas para mais adiante no projeto.Mas o teste contra o banco de dados tem seus problemas, o principal é que você está testando contra o estado compartilhado por muitos testes.Se você inserir uma linha no banco de dados em um teste, o próximo teste também poderá ver essa linha.
O que você precisa é de uma maneira de reverter as alterações feitas no banco de dados.
O Escopo de Transação A classe é inteligente o suficiente para lidar com transações muito complicadas, bem como transações aninhadas, onde seu código em chamadas de teste se compromete com sua própria transação local.Aqui está uma peça simples de código que mostra como é fácil adicionar capacidade de reversão aos seus testes:

    [TestFixture]
    public class TrannsactionScopeTests
    {
        private TransactionScope trans = null;

        [SetUp]
        public void SetUp()
        {
            trans = new TransactionScope(TransactionScopeOption.Required);
        }

        [TearDown]
        public void TearDown()
        {
            trans.Dispose();
        }

        [Test]
        public void TestServicedSameTransaction()
        {
            MySimpleClass c = new MySimpleClass();
            long id = c.InsertCategoryStandard("whatever");
            long id2 = c.InsertCategoryStandard("whatever");
            Console.WriteLine("Got id of " + id);
            Console.WriteLine("Got id of " + id2);
            Assert.AreNotEqual(id, id2);
        }
    }

Se você estiver usando LINQ to SQL como ORM, poderá gerar o banco de dados dinamicamente (desde que tenha acesso suficiente da conta usada para o teste de unidade).Ver http://www.aaron-powell.com/blog.aspx?id=1125

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top