Estou testando a unidade ou testando os meus procedimentos armazenados?

8

Recentemente, tive muitas ocasiões em que precisei manter procedimentos e funções armazenados complexos. Eles já estavam quebrados, geralmente de maneiras bastante sutis - havia muito poucas ocasiões em que você chamava o SP com parâmetros válidos e simplesmente não funcionava.

Minha solução foi desenvolver uma estrutura que execute os procedimentos armazenados dentro de uma transação, depois de inicializar o banco de dados para as condições iniciais necessárias, testando o resultado esperado, também dentro da mesma transação. A transação foi revertida no final do teste.

Isso funcionou muito bem. Mas alguns chamariam isso de "teste de integração", pois envolve a integração com o banco de dados. Eu chamo isso de teste de unidade, desde que testei componentes individuais e casos de teste individuais para esses componentes e desde que controlei completamente o estado inicial do banco de dados.

Mas onde deve ser traçada a linha? Isso é teste de integração ou teste de unidade? Existe uma razão prática para esse tipo de teste ser uma má idéia? Se este for "apenas" teste de integração, alguém tem sugestões sobre como fazer "testes de unidade" reais nesses procedimentos armazenados?


Atualização, três anos e meio depois. No meu projeto atual, comecei a usar testes de unidade SSDT, com sucesso, embora pudessem ser melhores. Consulte Verificando o código do banco de dados usando testes de unidade do SQL Server . Normalmente, eles implantam o projeto de banco de dados em sua instância do SQL Server LocalDB, portanto, isso remove qualquer dúvida sobre o ambiente de banco de dados que afeta o teste. Eu preencho o banco de dados com os dados necessários durante o pré-teste, o que remove perguntas sobre o conteúdo do banco de dados. Na verdade, eu uso as instruções MERGE para fazer isso, garantindo que todos os dados que não sejam necessários para o teste atual sejam removidos, inseridos ou atualizados no banco de dados antes do teste. Eles têm problemas:

  • Eles não são rápidos
  • Não é possível reutilizar as condições de teste
  • Não é possível reutilizar pré-testes (a menos que você os torne comuns a todos os testes em um projeto)
  • A interface do usuário pode ser melhorada

Uma das razões para os problemas acima é que eu ainda não reclamei deles. Eu recomendo que qualquer pessoa interessada tente esse recurso e depois reclame. É assim que as melhorias são feitas.

John Saunders
fonte
1
Por que seria um oxímoro ?
Doppelgreener
Boa pergunta. O que você acha de mudar o título? Talvez algo como isto: "Como procedimentos armazenados de teste de unidade"
Matthew Rodatus
@ Matthew: "edição colaborativa"
John Saunders

Respostas:

7

O objetivo do teste de unidade não é fazer com que uma fada mágica de teste de unidade venha e valide sua opinião. É para testar os blocos de construção menores e mais simples do seu sistema, para que você não esteja testando algumas funcionalidades mais abrangentes e tropeçou porque algo não funcionou da maneira que você pensava. Então, você pode dividir isso ainda mais? Eu não acho que você pode, nesse caso:

(a) Parece um teste de unidade para mim; e (b) Não importa se é um teste de unidade ou não, porque testa o que você precisa testar, que é o ponto de usar testes de unidade em primeiro lugar!

Se você acha que os procedimentos são muito complexos, convém dividi-los em partes menores (em procedimentos de banco de dados ou em código), mas, para fazer isso, obviamente você gostaria de ter esses testes em primeiro lugar!

Jack V.
fonte
7

O teste de unidade, por definição, gira em torno do teste de uma "unidade" de código em seu ambiente ou plataforma em execução. A plataforma que você está usando é um banco de dados, então você precisa usá-lo, não é integrador.

Uma pergunta melhor é por que você se importa. Desde que seja automatizado e agregue valor, o seu teste é realizado em um teste de unidade, em um teste de aceitação, em um teste de fumaça ou em um teste funcional?

No meu trabalho, atualmente alavancamos a estrutura de teste de unidade para fazer o ATDD (Acceptance Test Driven Development). Alguns testes são integrativos, outros não. Quase nenhum deles é teste de unidade, mas não nos importamos desde que:

  • o teste agrega valor
  • o teste não apresenta falhas ou sucessos falsos (é principalmente determinístico)
  • o teste é automatizado

Atualizar

É tudo uma questão de custo e benefício. Quanto mais partes móveis você tiver, maior o risco de ter um teste não determinístico. Os testes de unidade clássicos têm a menor quantidade de partes móveis e, portanto, a menor chance de serem não determinísticos. A desvantagem é que você não está testando os pontos de integração (código, banco de dados, fs, rede etc.). É útil ter tudo isso coberto em um teste, desde que o risco de falsa falha ou sucesso seja baixo o suficiente.

Os testes fornecem valor naquilo que eles não fazem. A chave é com que freqüência você terá falhas e sucesso falsos. Se eu tenho falhas falsas por 2 horas todos os meses porque essa é a janela de manutenção da rede, o valor dos testes é aparente. Quando eles falham, é óbvio que há algo errado com o recurso de rede, não um teste específico. E agora você tem mais cobertura de teste precisamente porque está testando esses pontos de integração.

dietbuddha
fonte
Uma das razões pelas quais me importo é que alguns lugares reclamarão se os testes de unidade não forem "testes de unidade" por definição. Outro motivo é que surgiu nos comentários sobre " Existe um meio termo? (Teste de unidade versus teste de integração) "
John Saunders
@ John Com base na resposta de @ dietbuddha e na pesquisa que li: Como você está utilizando a pilha de rede e o servidor de banco de dados, seus testes não são "principalmente determinísticos". Eles não devem ser classificados como testes de unidade. Mas é claro que é apenas a minha opinião.
Matthew Rodatus
1
@ Matthew: Não sei em quais plataformas você desenvolve, mas garanto que a pilha de rede e o servidor de banco de dados que uso não falham com muita frequência. De fato, falhas desses componentes podem ser ignoradas para fins de teste.
John Saunders
@ John Saunders: resposta atualizada
dietbuddha
2

Eu acho que depende de onde os dados vêm. Se você estiver criando suas condições de teste no prefixo, acho razoável chamá-lo de teste de unidade. Caso contrário, você terá argumentos minuciosos sobre o que conta como "pré-existente" para um teste de unidade. Assim como seus testes de unidade de banco de dados dependem da existência de tabelas de banco de dados e assim por diante, os testes de unidade fora do banco de dados dependem de coisas como definições de classe e, muitas vezes, instâncias dessas clssses. Isso não é ruim, é realista.


fonte
1

Eu consideraria isso um teste de integração:

  1. É muito mais lento do que deveria ser um teste de unidade.
  2. Ele não exerce uma única unidade atômica de código - exerce seu procedimento, o servidor de banco de dados, a pilha de rede etc.
  3. Não será fácil detectar onde ocorreu o erro se o teste falhar. Você precisará examinar cada falha manualmente para ver se foi uma falha do sistema externo ou seu código.

No entanto, testes como você descrevem são inestimáveis. Recentemente, começamos a adicionar testes assim, para testar procedimentos armazenados impossíveis de serem testados manualmente em determinados ambientes. Não conheço outra maneira de ter testes automatizados para procedimentos armazenados.

Portanto, esses testes são uma ótima idéia, mas os chame de testes de integração - use uma categoria ou sufixo de nome para diferenciá-los dos testes de unidade regulares. Dessa forma, você pode sinalizar falhas de teste de integração de maneira diferente das falhas de teste de unidade em sua automação de construção.

Matthew Rodatus
fonte
@ Matthew: Eu não testo o servidor de banco de dados ou a pilha de rede, assim como não testo o .NET BCL ou CLR. Além disso, não tive problemas em todas as falhas de localização. Talvez eu deva dar um exemplo mais detalhado do tipo de teste que quero dizer.
John Saunders
@ John Quando eu disse "teste o servidor de banco de dados e a pilha de rede", quero dizer que você está testando qualquer código que esteja exercitando. O .NET BCL / CLR deve ter um ticket para o teste de unidade - você não pode escrever código sem eles! Porém, um teste que fala com um servidor de banco de dados também está exercitando (ou seja, testando) o servidor e a rede. Acho que devo perguntar: como você está executando seus testes de procedimento? De um método de teste de unidade C #? Um script SQL? Como você automatiza os relatórios de resultados?
Matthew Rodatus
@ Matthew: um teste NUnit normal de um método na minha classe está exercitando o .NET Framework, por sua definição. Não é meu, pois estou assumindo que o .NET funcione. Da mesma forma, suponho que o SQL Server funcione, que a SqlCommandclasse funcione e que o transporte TCP funcione. Ainda não me enganei sobre isso.
John Saunders
1
@ Matthew: se o SQL Server e seus recursos e transportes internos não estiverem funcionando, eu tenho problemas maiores do que se eu estou testando a unidade ou a integração. Eu normalmente assumem que o sistema operacional funciona bem, e que a velocidade da luz é mais ou menos constante dentro dos quadros meus testes operar.
John Saunders
1
@ John Eu não acho que você entendeu meu ponto, mas tudo bem.
Matthew Rodatus
0

Isso é teste de integração ou teste de unidade?

Onde o teste é executado? No banco de dados ou por meio de equipamento de teste baseado em código (JUnit)?

No banco de dados, é provável que seja um teste de unidade (por ser autônomo); o equipamento de teste do tipo JUnit se conecta a um banco de dados externo e, como tal, um teste de integração.

Se este for "apenas" teste de integração

Por que as citações? Os testes de integração não são um tipo de animal que você nunca deve fazer.

alguém tem sugestões sobre como fazer "testes de unidade" reais nesses procedimentos armazenados?

Geralmente, acho que a única unidade razoável para um procedimento armazenado é o código de chamada¹ e o procedimento. Então, eu testei a integração do conjunto completo em um conjunto de testes que se parecem com testes de unidade.

Uma pergunta melhor é por que você se importa [com o nome].

Como ele permite que você divida os testes. Os testes de integração levam mais tempo para serem executados, exigem configuração adicional e provavelmente acesso a um recurso compartilhado por muitos testes (o banco de dados, por exemplo).

1) O código de chamada terá testes de unidade adicionais, se necessário.

mlk
fonte
Não há configuração adicional. Toda a configuração está no próprio teste. Além disso, esses testes são bem rápidos. Até o momento, a preparação no preâmbulo de teste não precisava fazer mais do que excluir 50 linhas e depois inserir outra dúzia.
John Saunders
Inicia um banco de dados se um não estiver em execução?
mlk
"Inicia um banco de dados"? Não, os testes usam uma string de configuração armazenada no arquivo de configuração para os testes, e presume-se que, uma vez determinada a string de configuração, ela continuará a se referir a um banco de dados válido dentro de um servidor de banco de dados válido. Em um mundo ideal, esses testes podem ser executados após a implantação (criação) de uma nova instância do banco de dados, usando a DDL atual, também armazenada no controle de origem. Estamos trabalhando para esse ideal.
John Saunders
É um bom ideal para chegar. Eu tenho trabalhado para o mesmo por algum tempo. Meu ponto foi que você tem tarefas de inicialização (iniciando um banco de dados). Você pode (e deve) fazer com que seus testes não falhem (Assert.Inconclusive no MSTest speak) se as tarefas não tiverem sido executadas. Esta área de teste de uma área cinza de teste de unidade. Escreva testes do tipo Unit Test, mas aceite que são testes de integração.
Mlk
1
Não sei o que você quer dizer com iniciar um banco de dados. No meu "mundo ideal", a implantação de uma nova instância vazia do banco de dados faria parte da construção automatizada, não parte dos testes.
John Saunders
-1

Microteste é um termo útil que pode ajudá-lo a contornar o debate sobre terminologia e se o seu teste é um teste de unidade ou não.

Uma definição comum de teste de unidade inclui que o sistema em teste é uma pequena unidade de funcionalidade e você deve poder executar todos os testes na memória, sem a ajuda de sistemas de arquivos, redes ou mecanismos de banco de dados . Se você precisar de um mecanismo de banco de dados real para executá-lo, não será um teste de unidade.

Embora essa definição funcione muito bem para todas as camadas de aplicativos, não é muito útil para a camada de acesso ao banco de dados mais baixa. Temos que reconhecer que é especial. Se seus testes testarem apenas pequenas unidades de funcionalidade de armazenamento / recuperação / atualização / exclusão, seus testes serão claramente tão valiosos quanto os testes de unidade "estritos" escritos nas camadas superiores do seu sistema.

azheglov
fonte
É aqui que a controvérsia existe: não estou testando o DAL: estou testando os procedimentos e funções armazenados no próprio banco de dados. Se o DAL é tão simples quanto configurar parâmetros e chamar o SP ou function or SELECT, então não me importo em testá-lo.
John Saunders