frente
Li várias coisas antes de fazer essa pergunta, incluindo muitas questões relevantes aqui no SE:
- (Software Engineering SE) Escrevendo testes para código cujo objetivo não entendo
- (Software Engineering SE) A equipe de novatos em testes de unidade precisa fazer o teste de unidade
- (Software Engineering SE) Práticas recomendadas para atualizar o código herdado com testes automatizados
- (Software Engineering SE) Como testar sistemas de grandes legados?
- (Postagem no blog) Como simular seu ambiente de teste de unidade
No entanto, não posso deixar de sentir que a coceira ainda não foi arranhada depois de ler para obter ajuda.
TL; DR
Como escrevo testes de unidade para código legado que não consigo executar, simular, ler ou entender facilmente? Quais testes de regressão são úteis para um componente que provavelmente funciona como planejado?
A figura inteira
Eu sou uma estagiária de verão de novo quando estou entrando na faculdade. Minha tarefa envolve estes requisitos:
- Para um produto específico, avalie se nossa equipe de software pode atualizar sua versão IDE e JUnit sem perder a compatibilidade com seus projetos existentes.
- Desenvolva testes de unidade para algum componente no código Java existente (em grande parte não é Java). Queremos convencer a equipe de software de que o teste de unidade e o TDD são ferramentas inestimáveis que eles deveriam usar. (No momento, há 0% de cobertura de código.)
- De alguma forma, termine os dias de codificação de cowboys para um sistema crítico.
Após obter uma cópia do código-fonte, tentei compilá-lo e executá-lo, para entender o que esse produto faz e como ele funciona. Não pude. Perguntei aos meus supervisores como eu faço e recebi uma nova máquina autônoma capaz de construí-la, incluindo os scripts de construção que realmente funcionam. Isso também não funcionou porque, como deveriam, o código de produção é executado apenas no sistema incorporado para o qual foi projetado. No entanto, eles têm um simulador para esse fim, então eles obtiveram o simulador e o colocaram nesta máquina para mim. O simulador também não funcionou. Em vez disso, finalmente recebi uma impressão de uma GUI para uma tela específica. Eles também não têm comentários de código em nenhum lugar no Java LOC de mais de 700.000, tornando ainda mais difícil de entender. Além disso, houve problemas ao avaliar se seus projetos eram ou não compatíveis com os IDEs mais novos. Particularmente, o código deles não foi carregado corretamente na versão IDE que eles usam.
Meu inventário está assim:
- NetBeans 8, 9, 10, 11
- JUnit 4, 5
- Seu código-fonte para um produto específico (inclui mais de 700.000 Java LOC)
- Praticamente nenhum comentário de código (ocasionalmente uma assinatura)
- Nenhum teste existente
- Uma foto física de uma janela da GUI
- Um documento de design de software (109 p.) Que não discute o componente na imagem
Eu pelo menos tenho o suficiente para escrever teoricamente testes que podem ser executados. Então, eu tentei um teste básico de unidade nesse componente. No entanto, não consegui inicializar os objetos que ele possuía como dependências, incluindo modelos, gerenciadores e conexões com o banco de dados. Como não tenho muita experiência em JUnit além dos testes básicos de unidade, siga-me na próxima seção.
O que aprendi com minha leitura
- Zombando: Se eu escrever um teste de unidade, provavelmente precisará ter variáveis simuladas para dependências de produção nas quais não consigo inicializar facilmente
setUp
. - Todos aqui liberalmente sugerem o livro "Trabalhando efetivamente com o código legado", de Michael Feathers.
- Provavelmente, os testes de regressão são um bom ponto de partida. Acho que não tenho armas suficientes para tentar o teste de integração, e os testes de regressão proporcionariam gratificação mais instantânea à nossa equipe de software. No entanto, não tenho acesso aos bugs conhecidos deles; mas eu poderia perguntar.
E agora uma tentativa de articular a incerteza que ainda tenho como questão. Essencialmente, não entendo como parte da escrita desses testes. Supondo que não receba nenhuma orientação adicional de meus supervisores (provavelmente), é da minha responsabilidade não apenas aprender o que esse componente faz, mas decidir quais testes são realmente úteis como testes de regressão.
Como profissionais que trabalharam com projetos como esse por mais tempo do que eu, você pode oferecer alguma orientação sobre como escrever testes de unidade nesse tipo de situação?
fonte
How do I write unit tests for legacy code that I can't build, run, simulate, read about, or otherwise understand?
Você não pode. Você precisa pelo menos saber qual é a saída esperada para uma determinada entrada.Respostas:
Para uma primeira aproximação, as partes interessadas de um conjunto de testes são os desenvolvedores / mantenedores de código. Você vai precisar de um pouco do tempo deles. Insista nisso.
Pergunte a eles sobre os problemas que estão enfrentando.
Pergunte a eles sobre os erros que eles corrigiram recentemente (nos últimos dois anos, supondo que esse seja um projeto longo e lento).
Tenho a impressão de que você não espera que eles sejam amigáveis ao seu trabalho; Talvez você esteja certo. Mas não acho que você possa criar algo útil sem a ajuda deles.
Eles vão segurar sua mão escrevendo o primeiro teste. Talvez você esteja arrastando-os, mas de mãos dadas de qualquer maneira.
Depois de fazer um teste, espero que fique mais claro como escrever o segundo. Se você conseguiu criar qualquer tipo de relacionamento, certamente será mais claro.
fonte
Eu vou assumir que em algum momento você pode obter o código para pelo menos compilar. Se você não consegue chegar tão longe, está na missão de um tolo.
A falta de requisitos, especificações ou capturas de tela adequados não é um bloqueador para a realização de testes. Contanto que você possa ler o código fonte, poderá escrever testes.
Se você tiver permissão para refatorar a base de código para isolar coisas como conexões de banco de dados por trás de sua própria interface, torna-se possível escrever alguns testes de unidade de caixa preta - basicamente escreva testes para lançar alguma entrada em um método e afirmar seu comportamento ou saída. Faça testes cobrindo cada linha de código em um método e peça a um dos membros seniores da equipe que revise seus testes.
Se você não tiver permissão para refatorar a base de código para escrever testes de unidade, testes de integração completa ou testes de automação da interface do usuário são sua única opção. Mesmo assim, o teste da caixa preta é sua melhor estratégia - insira algumas informações na interface do usuário e veja como elas reagem. Faça suas afirmações. Peça a um membro sênior da equipe que revise seus testes.
Em algum momento, você terá testes automatizados suficientes para começar a refatorar a base de código com confiança para introduzir testes de unidade. Os testes de interface do usuário garantem que os principais casos de uso de negócios funcionem e, em seguida, você pode adaptar uma arquitetura propícia ao teste de nível de unidade ou componente.
Outro benefício dos testes de interface do usuário é que você pode construir uma reputação com sua equipe que entende a base de código, que por sua vez os torna mais abertos para a introdução de alterações, porque a prova está no pudim. E você terá feito pudim escrevendo testes de aprovação.
Em resumo:
Você ficaria surpreso com a rapidez com que pode aprender a visão geral de um aplicativo de 700.000 linhas
fonte
Com base na descrição do problema e nos seus comentários, acho que o melhor que você pode fazer é começar com a API Java e tentar criar um único teste de unidade em torno de um método isolado.
Sem acesso ao código, só posso fornecer orientações limitadas, mas procuraria algo que: a) não tenha dependências; b) não faça alterações de estado. Por exemplo. digamos que exista um método que aceite um valor e verifique se ele cai em um determinado intervalo. Se você não conseguir encontrar algo sem dependências, tente algo que recupere um valor de uma dependência e tente zombar dele.
Depois de encontrar algo pequeno como esse, você pode começar a criar testes. Se o método testar um valor positivo, passe negativo e certifique-se de que ele seja capturado etc. O problema aqui é que talvez você não saiba ao certo qual é o comportamento correto. Você pode precisar pedir ajuda ou pesquisar na documentação para isso.
É improvável que você vá muito longe com isso. A realidade é que escrever código para que possa ser testado em unidade é uma arte para si. O código que não foi escrito com isso em mente pode ser difícil ou impossível de testar na unidade.
Outra opção que pode ser implementada com mais facilidade é o teste de compatibilidade binária. Ou seja, você captura as entradas e saídas de um sistema e, para testar, alimenta essas mesmas entradas e compara as saídas. Isso não informa se o código está certo ou errado para começar, mas pode ajudar a detectar erros de regressão em que as coisas mudaram involuntariamente ao fazer alguma outra modificação. Você precisará executar o aplicativo inteiro para que isso aconteça.
fonte