Como posso usar testes de unidade e TDD para testar um aplicativo que depende principalmente de operações CRUD de banco de dados?


22

No trabalho, um dos meus projetos trata principalmente de pegar dados passados ​​de um cliente externo e persistir em um banco de dados. É um aplicativo corporativo Java usando JPA e a maior parte de nossa lógica gira em torno das operações CRUD.

A maioria dos nossos erros envolve JPA de uma maneira ou de outra.

  • Exemplo 1: Se você clicar no botão Salvar duas vezes, o JPA poderá tentar inserir a mesma entidade no banco de dados uma segunda vez, causando uma violação da chave primária.
  • Exemplo 2: você recupera uma entidade do banco de dados, edita-a e tenta atualizar seus dados. A JPA pode tentar criar uma nova instância em vez de atualizar a antiga.

Muitas vezes, a solução precisa adicionar / remover / alterar uma anotação JPA. Outras vezes, tem a ver com a modificação da lógica do DAO.

Não consigo descobrir como obter confiança em nosso código usando testes de unidade e TDD. Não tenho certeza se é porque os testes de unidade e o TDD são inadequados ou se estou abordando o problema errado.

Os testes de unidade parecem um ajuste inadequado porque só consigo descobrir esses problemas em tempo de execução e preciso implantar em um servidor de aplicativos para reproduzir os problemas. Geralmente, o banco de dados precisa estar envolvido, o que considero estar fora da definição de um teste de unidade: estes são testes de integração.

O TDD parece um ajuste inadequado, porque o loop de feedback de implantação e teste é tão lento que me deixa muito improdutivo. O ciclo de feedback deploy + test leva mais de 3 minutos, e é apenas se eu executar os testes especificamente sobre o código que estou escrevendo. Para executar todos os testes de integração, leva mais de 30 minutos.

Há código fora deste molde e eu sempre teste unitário sempre que posso. Mas a maioria dos nossos bugs e os maiores sumidouros de tempo sempre envolvem o JPA ou o banco de dados.


Há outra pergunta semelhante , mas se eu seguisse o conselho, estaria agrupando a parte mais instável do meu código (o JPA) e testando tudo, menos ele. No contexto da minha pergunta, eu estaria na mesma situação ruim. Qual é o próximo passo após agrupar o JPA? A IMO que pergunta é (talvez) um passo para responder à minha pergunta, mas não uma resposta para ela.


4
O que você está fazendo é essencialmente um teste de integração, pois você precisa configurar o banco de dados para realmente testar. Eu posso imaginar que um módulo contaria com outros, para torná-lo ainda mais parecido com o teste de integração. Eu mudaria a pergunta que você tem sobre como aplicar abordagens TDD ao seu aplicativo.
InformedA

@randomA correto, editei minha pergunta para dizer isso explicitamente. Não entendo por que você está recomendando. Altero a pergunta. Você pode elaborar? Eu quero manter a parte de teste de unidade lá porque eu sim estar escrevendo testes de unidade do que os testes de integração (embora eu saiba que unit testing != TDD)
Daniel Kaplan

nada de especial, basta colocar TDD lá. Se você tiver um teste de unidade lá, muitas pessoas pensariam que você não entende nada, etc. não é bom para você .. #
InformedA


Respostas:


7

Uma opção é usar um banco de dados de teste na memória, como H2 ; tende a ser cerca de 10x mais rápido que um banco de dados padrão em uso de disco e com tempos de inicialização / desmontagem mais baixos.

Se isso ajudará depende em grande parte se os problemas de JPA que você está tendo são gerais o suficiente para que ainda falhem em bancos de dados diferentes. Não faz muito sentido executar testes mais rapidamente se eles perderem a maior parte dos problemas.

Mas se você puder fazer 10 corridas com H2 para cada uma com o sistema completo, isso poderá valer a pena.


É uma boa ideia, mas eu ainda teria que implantar no servidor de aplicativos, AFAIK. São muitos dos 3 ou mais minutos. Dito isto, definitivamente vale a pena fazer. Mas ainda é difícil imaginar a execução dos testes com a mesma frequência que eu executaria os testes de unidade e, portanto, parece ineficiente desenvolver usando o TDD.
Daniel Kaplan

1
Eu acho que geralmente existem maneiras de contornar esse requisito (por exemplo, docs.oracle.com/middleware/1212/toplink/TLADG/testingjpa.htm ). Muito alta chance de ser mais trabalhoso do que justificado; outra opção seria obter alguns servidores de teste mais robustos e executar as coisas em paralelo.
soru

1
@tieTYT Minha própria prova de conceito com a unidade hsqldb testando um aplicativo Web crud no github: TestingWithHsqldb - os testes de unidade não precisam ser implantados.

3

Os bancos de dados podem ser muito fáceis para o teste de unidade - você precisa de procedimentos e transações armazenados.

É o que a Microsoft diz sobre o teste de unidade de banco de dados . Você também pode executar testes de unidade em um banco de dados, gravando seus testes em Java ou C # configurando uma conexão com o banco de dados, iniciando uma transação, gravando no banco de dados todos os dados que deseja usar para o teste, executando os testes e depois revertendo. Nenhum dano ao banco de dados se você estivesse usando um para o qual também implantou e você obtém testes totalmente isolados.

Espero que isso possa lhe dar algumas dicas de como fazê-lo em sua estrutura.


Como eu disse, "A maioria dos nossos erros envolve JPA de uma maneira ou de outra.", Acho que o conselho do artigo perderia todos eles. Além disso, se você considerar esses testes de Java / C # ainda como testes de unidade, teremos definições muito diferentes. Eu acho que esse é um bom conselho em geral, mas ainda parece que levaria muito tempo para implantar e executar o conjunto e, portanto, não é propício ao TDD. Você discorda?
Daniel Kaplan

Costumávamos executar testes de unidade de banco de dados para nosso SQL, mas todos estavam em sprocs. Embora você possa testar o diretório sql de teste de unidade a partir de outros procedimentos sql, nossa estrutura de teste de unidade era o MSTest, por isso fazia sentido executá-los a partir daí (ei, temos marcações verdes no servidor de compilação, que foi o fator mais importante). Se você tem um banco de dados sempre ativo (e o fizemos de qualquer maneira), é fácil fazer o upload de todo o código sql e executar todos os testes de unidade no servidor de compilação. Às vezes você só precisa ser pragmático sobre essas coisas.
Gbjbaanb

Acho que você não respondeu à minha primeira frase.
Daniel Kaplan

bem, basta usar a unidade jpa então. Não sei responder como seu código JPA funciona (ou não), apenas tente lhe dar algumas idéias sobre como testar esse sql no banco de dados.
Gbjbaanb

3

Outras pessoas responderam com "Mock out your DB!" - mas qual é o objetivo de zombar da camada de banco de dados se você realmente precisa testar como ela interage com seu código?

O que você procura são testes de integração e / ou testes automatizados de interface do usuário. Você mencionou que o problema ocorre quando:

*If you click the save button twice*

A única maneira de testar isso é escrever um teste de interface do usuário automatizado para clicar no botão duas vezes. Talvez dê uma olhada no Selenium.

Provavelmente, você também precisará de um banco de dados de teste de unidade e, para os seus testes, aponte para isso. Uma dor para manter, mas bem-vindo ao TDD no mundo real.


isso se parece mais com um discurso retórico do que uma resposta
mosquito

Eu respondi a pergunta três vezes - testes de integração, testes de GUI e / ou um banco de dados de testes de unidade. Sim, é um discurso retórico, vou editá-lo com uma aparência de sanidade agora.
Rocklan

1
"A única maneira de testar isso é escrever um teste de interface do usuário automatizado para clicar no botão duas vezes. Talvez confira o Selenium." Em situações como essa, é melhor evitar que isso ocorra, caso contrário, a interface do usuário teria acesso direto ao banco de dados.
Daniel Kaplan

0

No exemplo apresentado na sua pergunta, você não pode testar a unidade / TDD para entrar na situação de clicar duas vezes no botão para causar um erro com muita facilidade. Mas o que você pode fazer no teste de unidade é que, no código que é chamado quando você clica no botão, se você receber uma exceção da camada de persistência, manipula-a adequadamente (zombando da camada de persistência ou usando um banco de dados na memória como foi sugerido em outras respostas) - repetindo novamente ou exibindo um erro ou o que for.

Você está certo de que o TDD pode começar a falhar quando você precisa executar testes que não são adequados para um teste de unidade (isto é, testes de integração / sistema) - isso formou bastante a discussão no recente "Is TDD Morto?" debates entre Kent Beck, Martin Fowler e David Heinemeier Hansson: http://martinfowler.com/articles/is-tdd-dead/

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.