Teste de unidade - aplicativo associado ao banco de dados

15

Qual seria a melhor abordagem para testar a unidade de um modelo que se integra a um aplicativo fortemente acoplado ao banco de dados?

O cenário específico aqui é um carrinho de compras - eu gostaria de poder testar a adição, remoção e recuperação de itens do carrinho, além da lógica de preços etc. Isso, na minha opinião, exige acesso ao banco de dados, embora eu tenha lido várias vezes que acesso ao banco de dados deve ser evitado.

user1189880
fonte
1
Interessante que as respostas que efetivamente dizer "reescrever seu código app" get votou-se
AD7six

Respostas:

10

A injeção de dependência é uma maneira de lidar com isso. Você pode configurar um banco de dados de teste para imitar o carrinho de compras ou até escrever algum código que "confirme" a transação do cliente. Então, em tempo de execução, seu software escolherá qual componente se conectar.

Apenas não conecte-se ao banco de dados de produção para nada durante o teste!

chrisaycock
fonte
1
Com o DI e o design adequado do aplicativo, você deve poder testar sem nenhum banco de dados - desde que a simulação que você injetar forneça uma simulação detalhada suficiente do banco de dados de back-end.
Peter K.
4

No teste de unidade, você precisa definir o limite do que está testando. O teste de unidade é diferente do teste de integração. Se a lógica de preços for independente do conteúdo do carrinho, teste-o separadamente. Se não for esse o caso, e todos os módulos estiverem fortemente acoplados, crie um ambiente de teste que imite a produção o máximo possível e trabalhe com isso. Não acredito que atalhos e simulações ajudem a longo prazo.

NoChance
fonte
2

O modelo não deve depender de um banco de dados (concreto). Se ele conhece apenas um banco de dados abstrato (leia "interface") que é entregue ao modelo, você pode substituir o banco de dados por um objeto simulado .

Na programação orientada a objetos , objetos simulados são objetos simulados que imitam o comportamento de objetos reais de maneira controlada. Um programador geralmente cria um objeto simulado para testar o comportamento de outro objeto, da mesma maneira que um projetista de carros usa um manequim de teste de colisão para simular o comportamento dinâmico de um ser humano nos impactos do veículo ...

EricSchaefer
fonte
1

Eu tive um problema semelhante - não tinha possibilidade de garantir que meu banco de dados de teste mantivesse os valores. Então, no futuro, recebo, por exemplo, outros preços.

Extraí os dados necessários para um pequeno sqlite -DB e usei esse banco de dados para meus testes. O banco de dados de teste agora faz parte da configuração do meu teste de unidade.

knut
fonte
2
O objetivo dos testes de unidade é testar seu código isoladamente. Se você usar um banco de dados sqllite, ele não estará isolado. Também inconsistências entre as bases de dados podem causar erros
Tom Squires
0

"Melhor" é subjetivo, mas você pode simplesmente usar uma conexão db de teste.

Use acessórios para carregar alguns dados de teste (exemplo de produtos a serem comprados) e depois escreva o caso de teste para a classe / função que você deseja testar.

AD7six
fonte
Descrever testes de unidade que testam uma função que atua em um banco de dados como testes de integração é bastante enganador @murph.
AD7six
1
Ok, agora estou profundamente confuso - se envolve um banco de dados, não é, na maioria das definições, um teste de unidade porque não é independente. Se você possui um banco de dados, está executando testes em um nível superior, um que possuísse dependências e analisasse "combinar" coisas. Independentemente disso, não é uma explicação clara para minha mente de como resolver o problema.
Murph
0

Eu construí um plugin para o Symfony 1.4 (PHP) para resolver esse problema (entre outros). Ele é modelado de acordo com a maneira como a estrutura de teste do Django (Python) opera : a estrutura cria e preenche um banco de dados de teste separado antes de cada teste iniciar e destrói o banco de dados de teste após a conclusão de cada teste.

Eu tinha algumas preocupações com essa estratégia, tanto em termos de desempenho (se o esquema não muda, por que não simplesmente limpar os dados em vez de reconstruir toda a estrutura?) E conveniência (às vezes eu quero inspecionar o banco de dados depois de um falha no teste, por isso não a destrua indiscriminadamente!), por isso adotei uma abordagem ligeiramente diferente.

Antes da execução do primeiro teste, o banco de dados é destruído e reconstruído, caso haja alterações no modelo desde o último teste. Antes de cada teste subsequente, os dados no banco de dados são limpos, mas a estrutura não é reconstruída (embora uma reconstrução manual possa ser acionada a partir de um teste, se necessário).

Ao carregar seletivamente acessórios de dados em cada teste, é possível criar o ambiente adequado para esse teste sem interferir nos testes subsequentes. Os arquivos de fixação também podem ser reutilizados, o que torna essa tarefa muito menos onerosa (embora ainda seja minha parte menos favorita dos testes de escrita!).

Nas duas estruturas de teste, o adaptador de banco de dados está configurado para usar a conexão de teste em vez da conexão "produção" para impedir que a execução do teste corrompa os dados existentes.


fonte
0

Eu diria, basta ir em frente e usar equipamentos para pré-carregar os dados. É como as estruturas de teste de unidade parecem funcionar em geral, ao testar a manipulação de dados.

Mas se você realmente deseja evitar ter que se conectar a um banco de dados de qualquer tipo e seguir a definição excessivamente rigorosa de que os testes de unidade não tocam em nada fora do código, dê uma olhada na zombaria de objetos - isso pode lhe dar idéias.

Por exemplo, em vez de soltar o SQL diretamente no código em que você precisa, tenha uma maneira de chamar um método que faça apenas o que esse SQL faz. Use Person.getPhoneNumber(), por exemplo, em vez de SELECT phone_number FROM person WHERE id = <foo>. Não apenas é mais fácil e fácil entender rapidamente, mas durante o teste, você pode zombar do objeto Person para que getPhoneNumber()ele sempre retorne 555-555-5555ou algo assim, em vez de tocar no banco de dados.

Izkata
fonte
0

Isso é bastante fácil com junit, se um pouco longo.

A "configuração" deve definir e preencher um conjunto de tabelas temporárias.

Você pode executar os testes de unidade para todas as funcionalidades de atualização, inserção e exclusão.

Para cada teste, você chama seu método de atualização e, em seguida, executa um SQL para verificar o resultado esperado.

Na fase de "desmontagem", você solta todas as tabelas.

Dessa maneira, você sempre executa os mesmos testes nos mesmos dados iniciais. Se você mantiver as tabelas entre os testes, elas acabam sendo "poluídas" por testes com falha, também, um teste consistente de "inserção" é quase impossível, pois você precisa continuar inventando novas chaves em todos os testes.

James Anderson
fonte