Testes: por que a automação de testes é um elemento crítico dos projetos de transformação legados

Tudo o que você sabe sobre os testes de aplicativos se aplica aos projetos de transformação legados, desde testes de unidade a testes de caixa preta/branca, plano de teste, etc. E quando você termina com tudo isso vem a parte difícil: a prova de identidade.

Para entender os testes em um projeto de modernização legado, precisamos entender como a conversão e a modernização automatizadas de linguagens funcionam e em qual etapa o teste ocorre.

A transformação do aplicativo é um processo iterativo. Pegamos o código legado, analisamos (analyze & parse), convertemos para Java, construímos, testamos e quando alguns testes não passam, modificamos o mecanismo de conversão. O processo é explicado na imagem abaixo.

Este post do blog está falando sobre os pequenos pedaços de laranja na imagem.

A questão fundamental dos projetos de conversão legados: como podemos determinar se o aplicativo convertido faz exatamente o que o aplicativo legado fazia. Falando sobre o termo ‘exatamente’ e o que isso significa, isso não é como 2 + 2 = 4, mas é mais parecido com 2,00034 + 2,00066 = 4,00, caso a implementação da linguagem legada subjacente assim o exigir. Arredondamento, truncamento, precisão, manipulação de over/underflow são questões a serem consideradas. Pode haver diferenças entre as implementações das linguagens legadas no mainframe e as implementações das mesmas nos sistemas abertos. Existem bugs em implementações legadas e devemos implementa-los literalmente! (Sim, a gente também ficou surpreendida (?) quando encountrou fornecedores de renome com bugs, várias vezes.)

‘O comportamento exato’ um pouco mais formal pode ser definido assim: produzir a mesma saída usando a mesma entrada. Em essência, temos que estabelecer um estado conhecido do sistema legado, chamamos isso ‘a linha de base’. Em seguida, capturamos e gravamos sequências de entrada, processamo-las e capturamos e registramos a saída. Isso pode assumir a forma de logs, sequências de pressionamentos de tecla, capturas de tela, mensagens de protocolo (por exemplo, conversas http), conjuntos de dados, dumps de banco de dados ou qualquer coisa que importe e forneça informações significativas para a comparação. É simples. Executamos os mesmos testes de negócios no aplicativo java convertido e quando a saída corresponde à saída do sistema de linha de base, estamos bem. Mas estamos mesmo?!

Os desafios – como sempre – são muitos.

O cliente tem acesso ao sistema legado. Eles conhecem o seu próprio negócio; eles sabem o que é importante. Eles devem entender que o sucesso do projeto depende dessa exatidão que discutimos acima. Se o cliente criar nove casos de teste pelos quais os testes devem passar, a responsabilidade é dele. Se eles criarem 499, isso não faz diferença nenhum para nós quando executamos os casos de teste – potencialmente encontramos mais discrepâncias, e pronto – mas faz uma diferença enorme no que podemos supor sobre a qualidade da conversão. Além disso, os casos de teste devem cobrir toda a amplitude dos aplicativos. A fidelidade nunca será 100% exatamente, mas se aproximará assintoticamente de 100% quanto mais casos de teste tivermos. É claro que um equilíbrio pode ser estabelecido entre o risco de deixar uns bugs escaparem versus os custos de criar novos casos de teste e os custos de avaliá-los. Repetidamente.

Esta postagem do blog não aborda o papel do cliente em detalhes quando se trata de criar casos de teste significativos. Mas este sim.

Um projeto de conversão não é um assunto que acontece de uma noite para o outro dia. Os clientes continuam desenvolvendo os seus sistemas mesmo após a assinatura do contrato para o projeto de conversão. Estabelecer uma linha de base pode ser um desafio neste ambiente. Um movimento errado – e a metade das gravações originais do caso de teste devem ser descartadas e forçadas a serem feitas novamente. Bom. Mas qual metade exatamente?

Velocidade: para uma nova execução de teste, a linha de base deve ser restaurada no sistema de teste. Essa restauração deve ser razoavelmente rápida. E como definir ‘razoavelmente’?

Uma outra nota sobre velocidade: performance. O código convertido pode não ser tão rápido quanto o original no início do teste. Não estamos a falando aqui de pequenos lotes que duram 5 em vez de 3 segundos. A menos que seja repetidamente chamado 5000 vezes. Essa diferença pode ser crítica durante o teste, particularmente se o ambiente de teste não estiver nem perto do desempenho do ambiente de produção. Para poder detectar problemas de desempenho, devemos ter um ambiente comparável ao de produção. Ou ao contrário: o desempenho medido em um ambiente de teste dita os requisitos para o ambiente de produção. (Escala automática, alguém conhece?)

Ferramentas: Precisamos de ferramentas bimodais, ou seja, captura na linha de base e reprodução em sistemas convertidos. As ferramentas usuais como Selenium, Appium e similares não vão funcionar.

Esta postagem do blog não discute os testes de integração. Isso é um reino novo de desafios. Esses testes acontecem no site do cliente após o lançamento. Mas não podemos esperar pelos resultados desses testes porque pode demorar uma eternidade e isso retarda o desenvolvimento drasticamente. Eles introduzem dependências que não podemos controlar internamente. Imagine que o aplicativo acessa alguns dados por meio de FTP ou APIs, ou um outro aplicativo é chamado antes/depois. Precisamos de um ciclo muito mais curto para poder testar internamente. O código convertido deve ser isolado dessas dependências. O desafio aqui é que é o cliente que deve criar esses casos de teste para nós.

E todos esses processos de teste devem ser automatizados porque em um projeto de conversão nós os executamos continuamente e usamos CPU para testar cerca de dez vezes mais do que para conversão e compilação. Usamos o nosso AppTester para fazer o trabalho pesado.