Bancos de dados e testes de unidade / integração

25

Eu tive uma discussão com alguém sobre testes de unidade / integração com aplicativos da web e tenho uma discordância sobre uma ideia central. O problema é que a pessoa com quem estou conversando acha que o banco de dados no qual o teste de unidade trabalha deve ter dados pré-preenchidos e acho que deve estar completamente vazio antes e depois da execução dos testes.

Minha preocupação com dados pré-preenchidos no banco de dados é que não há como garantir que os dados sejam mantidos em bom estado. Os testes em si vão criar, excluir e modificar dados no banco de dados, então não vejo como ter dados no banco de dados antes de iniciar os testes é uma coisa boa.

Parece que a melhor maneira de testar a funcionalidade do banco de dados seria ter as seguintes configurações:

  1. Em uma fase de "configuração" antes da execução do teste, você primeiro trunca todas as tabelas no banco de dados
  2. Em seguida, insira todos os dados necessários para os casos de teste que você está prestes a executar
  3. Então você executa e valida os casos de teste
  4. Em uma fase de "desmontagem", você trunca mais uma vez todas as tabelas no banco de dados

Não vejo outra maneira melhor de garantir que os dados contra os quais você está testando sejam um bom teste testável.

Estou faltando alguma coisa aqui? Essa não é a melhor maneira de testar a funcionalidade relacionada ao banco de dados? Existe algum benefício em ter um banco de dados pré-preenchido que sempre exista no banco de dados (mesmo antes de iniciar os testes ou após a conclusão dos testes)? Qualquer ajuda em idéias para explicar meu processo de maneira diferente, para melhor entender meu ponto de vista, também seria ótima (isto é, se meu argumento tiver mérito).

ryanzec
fonte
veja também: Técnicas de Teste de Software
mosquito

Respostas:

21

Para mim, os testes de unidade não devem lidar com o banco de dados, os testes de integração lidam com o banco de dados.

Os testes de integração que lidam com o banco de dados devem, na prática, ter um banco de dados vazio com uma abordagem de desmontagem e desmontagem, o uso de uma abordagem baseada em transações é um bom caminho a percorrer (por exemplo, criar uma transação na configuração e reversão na desmontagem).

O que seu amigo parece querer fazer é testar do ponto de vista da "regressão", ou seja, ter dados reais lá e ver como o sistema reage, afinal, nenhum sistema é perfeito e geralmente pode haver dados ruins espalhados por algum lugar que forneçam algumas peculiaridades do seu modelo de domínio.

Suas práticas recomendadas são o caminho a seguir, e o que eu costumo fazer é encontrar um cenário para dados incorretos, escrever um teste de integração com uma configuração e desmontar esse cenário exato.

Nicholas Mayne
fonte
Eu apenas tenho certeza de que diferença existe entre teste de unidade e teste de integração, além de ouvir que a unidade deve usar dados simulados e a integração deve usar um banco de dados (iniciado outro thread programmers.stackexchange.com/questions/101300/… para descobrir a diferença ) Fora isso, tudo o que você está dizendo parece estar alinhado com o que estou pensando.
Ryanzec
Não tem problema, eu adicionei mais informações à sua outra resposta
Nicholas Mayne
1
por que você não pode testar o banco de dados? Se você colocar seu SQL em procedimentos armazenados, poderá testá-los com dados definidos por teste e, de repente, tudo ficará fácil. Esta é definitivamente uma das melhores práticas mais pessoas devem seguir, veja o que MS diz
gbjbaanb
1
integration tests- O que você quer dizer? Como mencionei, os módulos que usam banco de dados podem e devem ser testados com testes de unidade. Banco de Dados me pode zombou manualmente ou substituída por na memória implementação
Hellboy
6

Se seus testes dependem do banco de dados, acho que é mais importante que os dados de seu interesse estejam em um estado conhecido para seus testes, em vez de o banco de dados estar vazio. Uma das medidas dos bons testes é que cada teste deve falhar por um motivo e nenhum outro teste deve falhar pelo mesmo motivo.

Portanto, se seus testes se preocupam com o estado dos dados, coloque-os no estado conhecido e retorne os dados para esse estado após a execução dos testes, para que seus testes sejam reproduzíveis.

Se você pode dissociar seus testes do estado dos dados, zombando, isso também seria uma coisa boa. Você mencionou que está fazendo testes de unidade / integração, mas é claro que essas duas coisas devem ser consideradas separadamente. Seus testes de unidade devem ser desassociados do banco de dados, se possível, e seus testes de integração devem ser testados com o banco de dados em um estado conhecido.

Paddyslacker
fonte
2

Bem, vejo um benefício em ter um banco de dados pré-preenchido: você não precisa escrever o código que irá inserir os dados necessários, pois ele existe. Caso contrário, existem apenas desvantagens. Talvez alguém tenha modificado os dados de teste no banco de dados? Talvez alguém tenha tentado atualizar os dados? Mas o pior é ter um caso de teste bagunçando o banco de dados ... Você acaba recriando o banco de dados inteiro manualmente várias vezes.

Você está certo em como os testes devem ser escritos, exceto que eu não truncaria nada:

  • fase de configuração: conecte-se ao banco de dados e insira os dados
  • fase de execução
  • fase de desmontagem: remova os dados inseridos (truncar)

Agora, esse cenário é ótimo para testes de unidade. Quando precisamos de dados para testes de unidade e integração, descobri que uma grande fase de configuração comum a todos os casos de teste (reagrupamos todas as "inserções" em um método estático) também pode funcionar muito bem. É como um meio termo entre a sua ideia e a do seu amigo. A única desvantagem é que você deve ter muito cuidado ao adicionar alguns dados novos para não quebrar os casos de teste existentes (mas se você adicionar duas ou três linhas por tabela, como fizemos, isso não deve ser um problema)

Jalayn
fonte
Eu não preferiria criar as partes do banco de dados necessárias para o teste, do que alguém modificar acidentalmente os dados de uma maneira que cause uma falha? Ter que garantir que os dados estejam corretos quando um teste falhar parece algo que pode ser evitado.
Ryanzec 15/08
1
A grande fase de configuração que insere dados úteis para diferentes casos de teste pode ser útil apenas para testes de integração nos quais você precisa verificar diferentes partes do aplicativo trabalhando juntas. Pode valer a pena ter esse grande conjunto comum de "inserções" porque você provavelmente precisará de algumas delas para outros testes de integração. Caso contrário, se estamos falando apenas de testes de unidade puros, sou absolutamente a favor de ter um conjunto de dados para inserir para cada caso de teste.
achou
1

Eu acho que você precisa refinar um exemplo com seu colega e descobrir exatamente o que eles significam. Vocês dois podem estar na mesma página.

Exemplo: tabela de transações da conta corrente

  1. Você não deseja testar a exibição desta tabela para um usuário / conta sem transações?
  2. Teste a adição do primeiro registro e veja se você pode criar um saldo.
  3. Crie registros quando já houver registros e verifique o saldo de execução e quaisquer outras regras de negócios.
  4. Exibir tabela com registros existentes e todos os outros CRUD.

Se você conseguir isso executando as etapas 1 e 2 ou iniciando com um banco de dados já nesse estado (restaurar um backup?), Não sei se isso importa. Sua idéia de criar scripts para mim facilita o gerenciamento de quaisquer alterações necessárias (como se você se esquecesse de criar uma conta de administrador e precisasse dela para um novo usuário). Os arquivos de script são mais fáceis de colocar no controle de origem do que algum arquivo de backup. Isso também é afetado pela distribuição ou não deste aplicativo.

JeffO
fonte
0

Para desenhar aspectos de algumas respostas juntos e adicionar meu 2p ...

Nota: meus comentários se referem principalmente ao teste do banco de dados , e não ao teste da interface do usuário (embora se aplique obviamente semelhante).

Os bancos de dados precisam tanto de teste quanto os aplicativos de front-end, mas tendem a ser testados com base em 'funciona com o front-end?' ou 'os relatórios produzem o resultado correto?', que na minha opinião está sendo testado muito tarde no processo de desenvolvimento do banco de dados e não é muito robusto.

Temos vários clientes que utilizam testes de unidade / integração / sistema para o banco de dados do data warehouse, além do UAT / performance / et al. testes. Eles descobrem que, com uma integração contínua e testes automatizados, enfrentam muitos problemas antes de chegar ao UAT tradicional, economizando tempo no UAT e aumentando as chances de sucesso do UAT.

Tenho certeza que a maioria concorda que um rigor semelhante deve ser aplicado aos testes de banco de dados e aos testes de front-end ou de relatório.

O principal com o teste é testar pequenas entidades simples, garantindo sua correção, antes de prosseguir para combinações complexas de entidades, garantindo sua correção antes de expandir para o sistema mais amplo.

Então, dando algum contexto à minha resposta ...

Teste de Unidade

  • possui um foco de teste para provar que a unidade funciona, por exemplo, uma tabela, exibição, função, procedimento armazenado
  • deve 'stub' as interfaces para remover dependências externas
  • fornecerá seus próprios dados. Você precisa de um estado inicial conhecido dos dados; portanto, se houver uma chance de os dados existirem antes do teste, truncamentos / exclusões devem ocorrer antes da população
  • será executado idealmente em seu próprio contexto de execução
  • limpará depois de si próprio e removerá os dados usados; isso é importante apenas quando stubs não são usados.

As vantagens de fazer isso são que você está removendo todas as dependências externas no teste e executando a menor quantidade de testes para provar a correção. Obviamente, esses testes não podem ser executados no banco de dados de produção. Pode ser que haja vários tipos de testes que você fará, dependendo do tipo de unidade, incluindo:

  • verificação de esquema, alguns podem chamar isso de teste de 'contrato de dados'
  • valores da coluna passando
  • o exercício de caminhos lógicos com diferentes valores de dados para funções, procedimentos, visualizações, colunas calculadas
  • teste de casos extremos - NULL, dados incorretos, números negativos, valores muito grandes

Teste de Integração (Unidade)

Achei este post do SE útil ao falar sobre vários tipos de teste.

  • tem o foco de teste para provar que as unidades se integram
  • realizada em várias unidades juntas
  • deve 'stub' as interfaces para remover dependências externas
  • fornecerá seus próprios dados, para remover os efeitos de influências externas de dados
  • será executado idealmente em seu próprio contexto de execução
  • irá limpar depois de si e remover os dados criados; isso é importante apenas quando stubs não são usados.

Ao passar de testes de unidade para esses testes de integração, geralmente haverá um pouco mais de dados, para testar uma variedade maior de casos de teste. Obviamente, esses testes não podem ser executados no banco de dados de produção.

Em seguida, ele passa para o Teste do Sistema , Teste de Integração do Sistema (também conhecido como teste de ponta a ponta), com volumes de dados e escopo cada vez maiores. Todos esses testes devem se tornar parte de uma estrutura de teste de regressão. Alguns desses testes podem ser escolhidos pelos usuários para serem executados como parte do UAT, mas UAT são os testes definidos pelos usuários , não como definidos pela TI - um problema comum!

Então agora que eu dei algum contexto, para responder às suas perguntas reais

  • dados pré-preenchidos para testes de unidade e integração podem causar erros espúrios e devem ser evitados.
  • A única maneira de garantir testes consistentes é não fazer suposições sobre os dados de origem e controlá-los rigorosamente.
  • é importante um contexto de execução de teste separado, para garantir que um testador não esteja em conflito com outro testador executando os mesmos testes em uma ramificação diferente do código do banco de dados controlado de origem.
Marcus D
fonte
-3

Francamente, acho que se você fizer testes de unidade sem um banco de dados aproximadamente do mesmo tamanho do banco de dados de produção existente, terá muitas coisas que passam nos testes e falham na produção para obter desempenho. É claro que sou contra as pessoas que desenvolvem um pequeno banco de dados local por esse motivo também.

E se o código é específico de dados, como você pode testá-lo efetivamente sem dados? Você sentirá falta de ver se as consultas retornaram os resultados corretos. Por que você gostaria de considerar o teste em um banco de dados vazio, tudo o que indica é se a sintaxe está correta e não se a consulta está correta. Isso parece míope para mim. Já vi muitas coisas que são executadas e passam em testes categoricamente errados. Você não quer encontrar isso em testes de unidade? Eu faço.

HLGEM
fonte
Não estou sugerindo a execução em um banco de dados vazio, se você vir a etapa 2, tenho "Em seguida, insira todos os dados necessários para os casos de teste que você está prestes a executar". Sobre a questão do desempenho, acho que não é para isso que serve o teste de unidade, ou seja, mais testes de carga. Teste de unidade para mim, para testar, para garantir que o lógico do seu código funcione. se a lógica funcionar, funcionará para 1 registro ou 100.000.000.000 de registros dos mesmos dados básicos (pensou que seria muito mais lento).
Ryanzec
As consultas ao banco de dados não são apenas lógicas e, quanto mais cedo você descobrir, não funcionará melhor. O que funciona para um registro geralmente atinge o tempo limite no prod e o teste de unidade deve mostrar isso o mais rápido possível.
HLGEM 15/08/11
Os testes de unidade e integração são para funcionalidade e não desempenho, para que você possa testar com pequenas quantidades de dados
user151019
O teste de unidade nunca deve usar um banco de dados - os testes de integração usam bancos de dados.
Nicholas Mayne
1
Na verdade, você está falando sobre testes de carga. Se você tivesse um conjunto de testes de aceitação e os conectasse a uma ferramenta de teste de carga, seria capaz de obter o efeito desejado.
Nicholas Mayne