Estou iniciando um novo projeto e tentando muito com muito esforço usar o TDD para conduzir o design. Estou pressionando há anos e finalmente obtive aprovação para dedicar mais tempo a esse projeto para usá-lo enquanto aprendo como fazê-lo corretamente.
Este é um novo módulo, vinculado a um sistema existente. Atualmente, todo o acesso a dados acontece por meio de serviços da web, que na maioria das vezes são apenas um invólucro fino sobre os procedimentos armazenados no banco de dados.
Um requisito é que, para uma determinada loja, eu retorne todos os pedidos considerados válidos para este aplicativo. Um pedido de compra é considerado válido se a data de envio cair dentro de um determinado intervalo a partir da data de abertura das lojas (isto é para novas lojas).
Agora, não posso colocar essa lógica no código do aplicativo, pois não vou trazer de volta um milhão de POs apenas para que a dúzia que se aplica possa se aplicar a esse armazenamento, devido à restrição acima.
Eu estava pensando em poder passar o período para um processo GetValidPOs e fazer com que eles usem esses valores para retornar os POs válidos. Mas, e se adicionarmos outro requisito ao que é considerado um pedido válido?
E como faço para testar isso e verificar se continua funcionando? Não estamos usando um ORM, e é improvável que isso aconteça. E não posso ligar para o banco de dados no meu teste.
Estou preso.
Meu outro pensamento, é ter algumas simulações que retornam dados válidos, outras que retornam alguns dados inválidos e o repositório local lança uma exceção se ocorrerem dados incorretos e testa se a exceção é lançada se dados inválidos forem retornados pelo processo GetValidPOs (ou a simulação usada nos testes).
Isso faz sentido? Ou há um jeito melhor?
ATUALIZAÇÃO: Eu sou capaz de usar EF parece. Agora só preciso descobrir como usá-lo e torná-lo testável, enquanto ainda posso confiar nos procedimentos armazenados e na dificuldade de ter dados espalhados por vários bancos de dados.
fonte
Respostas:
Esta é uma desvantagem importante dos procedimentos armazenados na era do TDD. Eles têm algumas vantagens reais, mesmo agora, mas, por definição, qualquer teste que exerça um procedimento armazenado não é um teste de unidade; é um teste de integração na melhor das hipóteses.
A solução usual, supondo que a arquitetura não possa ser alterada para usar um ORM, é não colocar esses testes no conjunto de testes de unidade; em vez disso, coloque os testes em um conjunto de integração. Você ainda pode executar o teste sempre que quiser verificar se ele funciona, mas como o custo inerente à configuração do teste (inicializando um banco de dados com os dados de teste adequados) é alto e afeta os recursos que o agente de teste de unidade do seu build-bot pode não ter acesso a, ele não deve estar no conjunto de testes de unidade.
Você ainda pode testar o código da unidade que requer os dados, abstraindo qualquer coisa que não puder testar na unidade (classes ADO.NET) em uma classe DAO que você pode zombar. Em seguida, você pode verificar se as chamadas esperadas são feitas consumindo código e reproduzindo o comportamento do mundo real (como não encontrar resultados), permitindo o teste de vários casos de uso. No entanto, a configuração real do SqlCommand para chamar o processo armazenado é praticamente a última coisa que você pode testar em unidade, cortando a criação de comandos da execução de comandos e zombando do executor de comandos. Se isso soa como muita separação de preocupações, pode ser; lembre-se: "não há problema que não possa ser resolvido por outra camada de indireção, exceto por ter muitas camadas de indireção". Em algum momento, você deve dizer "basta; eu simplesmente não posso testar isso, nós
Outras opções:
Teste o processo armazenado usando uma instância DBMS "de curta duração" como SQLite. Geralmente é mais fácil fazer isso ao usar um ORM, mas o teste pode ser feito "na memória" (ou com um arquivo de banco de dados predefinido incluído no conjunto de testes). Ainda não é um teste de unidade, mas pode ser executado com um alto grau de isolamento (o DBMS faz parte do processo em execução e não é algo ao qual você se conecta remotamente, que pode estar no meio do conjunto de testes conflitante de outra pessoa). A desvantagem é que as alterações no processo armazenado podem ocorrer na produção sem que o teste reflita a alteração; portanto, você deve ser disciplinado em garantir que a alteração seja feita primeiro em um ambiente de teste.
Considere atualizar para um ORM. Um ORM com um provedor Linq (praticamente todos os de uso comum têm um) permitiria definir a consulta como uma instrução Linq; essa declaração pode ser dada a um Repositório zombado que possui uma coleção de dados de teste na memória para aplicá-lo. Assim, você pode verificar se a consulta está correta sem sequer tocar no banco de dados (ainda deve executar a consulta em um ambiente de integração, para testar se o provedor Linq pode digerir corretamente a consulta).
fonte
Meu conselho é dividir e conquistar . Por enquanto, esqueça o banco de dados e a persistência e concentre-se em testar implementações falsas de seus repositórios ou objetos de acesso a dados.
Eu zombaria do repositório que retorna pedidos de compra. Crie uma simulação com vinte pedidos de compra ímpares.
Stub uma chamada para GetValidPOs para que ele chame seu procedimento simulado, em vez de banco de dados.
Você precisa de um teste de unidade para garantir que os dados corretos sejam retornados de uma simulação.
Você também precisa de um teste de integração para garantir que os dados corretos sejam retornados de um banco de dados. O teste de integração exigiria alguma configuração e limpeza. Por exemplo, antes de executar o teste de integração, propague seu banco de dados executando um script. Verifique se o seu script funcionou. Consulte o banco de dados chamando seus procedimentos armazenados. Verifique se seus resultados estão corretos. Limpe o banco de dados.
Como eu já disse, você precisa de uma simulação que retorne pelo menos alguns dados que você possa consultar.
Ao consultar dados, você deseja garantir que seu sistema possa lidar com exceções normalmente. Portanto, você zomba do comportamento para que ele gere exceções em determinados cenários. Você então escreve testes para garantir que seu sistema possa lidar com essas exceções normalmente.
fonte
Assim como testar a unidade Java ou Javascript significa escrever testes de unidade usando a linguagem Java para java, e testar funções de Javascript com Javascript, escrever testes automatizados para levá-lo a escrever procedimentos armazenados significa que a biblioteca de testes unitários que você está procurando se baseia procedimentos.
Dito de outra maneira, use procedimentos armazenados para testar procedimentos armazenados porque:
Assim como o TDD em uma linguagem OO, você deseja que seu teste de unidade configure apenas uma linha ou mais de dados para testar o que é necessário para o procedimento (minimalismo, apenas ter o que seus testes simples precisam). O resultado disso é que você terá vários testes de unidade simples para cada procedimento armazenado. Esses testes simples serão mais fáceis de manter do que os testes complicados que dependem de um grande conjunto de dados que não é facilmente mapeado de volta para o que o teste realmente precisa.
fonte