Refatorando código legado em projetos React — Parte I

Bruno Nardini
Bemobi
Published in
7 min readMay 10, 2019
Fotografia disponibilizada por Pixabay

Código legado é uma dor que atinge em cheio o coração de qualquer programador. Com a evolução do desenvolvimento WEB, tecnologias emergentes trouxeram grande robustez para o que hoje chamamos de front-end, com isso vieram novos desafios de como escrever um código limpo. Nesta série de artigos mostrarei algumas dicas de como trabalhar em um projeto React com código legado.

Mas o que é um código legado?

Nas minhas leituras não encontrei um senso comum na definição, para alguns autores código legado é um código herdado, é um código de outra pessoa. Para outros autores é usado como gíria para um código mal escrito, que é difícil de dar manutenção. Minha definição favorita é do Michael Feathers, para ele todo código legado é um código sem teste.

Code without tests is bad code. It doesn’t matter how well written it is; it doesn’t matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don’t know if our code is getting better or worse.
Working Effectively with Legacy Code — Michael Feathers.

Ou seja, você pode estar escrevendo um código hoje e, se ele não tiver teste automatizado, então ele já está nascendo como um código legado. Por mais severo que isto seja, não importa se o código está bem escrito, qual tecnologia foi usada ou quem o escreveu, sem teste não há garantias de que a aplicação não vai apresentar bugs em uma manutenção ou melhoria.

O Projeto

Para servir de exemplo eu criei um projeto bem simples, você só precisar ter o Node.js instalado para poder executá-lo. Ele se encontra no seguinte repositório do GitHub:

https://github.com/megatroom/refactoring-react-legacy-code

Ideal que você baixe a versão 0.1.0 para me acompanhar na refatoração:

https://github.com/megatroom/refactoring-react-legacy-code/releases/tag/v0.1.0

Ou você pode usar o git da seguinte forma:

git clone git@github.com:megatroom/refactoring-react-legacy-code.git
cd refactoring-react-legacy-code
git checkout v0.1.0

Para simular o mundo real, o projeto contém uma API Rest para podermos testar a integração do front-end com o back-end. Não há dependências externas, o controle de usuário é feito em memória e a lista de cursos é um JSON fixo.

Execute a API, usando o comando abaixo na raiz do projeto:

cd api
npm install
npm start

Com a API em execução, em um outro terminal, execute o comando na raiz do projeto para executar o client:

cd client
npm install
npm start

Irá abrir uma aba no seu navegador com o projeto pronto para ser usado. Para fins didáticos deste artigo, o projeto possui apenas este fluxo:

Figura 1 — Fluxo da aplicação

É necessário cadastrar um usuário para fazer o login.

Os primeiros passos

Antes de pensar em alterar ou escrever qualquer código, é importante entender como a aplicação está funcionando, independente se está com bug ou não. Por mais óbvio que seja, o primeiro passo é executar o projeto e fazer um teste manual.

Eu anoto cada passo que fiz para executar o projeto na minha máquina, como instalação de dependências, variáveis de ambiente, configuração do docker, tudo o que for necessário para conseguir testar o fluxo principal da aplicação. No final eu uso as anotações para atualizar a documentação. Mas como estamos falando de legado, normalmente não há documentação, então eu transcrevo tudo para markdown e deixo no Readme.md na raiz do projeto, que fica fácil de qualquer desenvolvedor achar quando entrar no repositório.

A qualidade de um projeto não deve estar apenas em seu código, mas em todo seu ecossistema, como documentação, configuração e arquitetura.

Toda aplicação possui um fluxo importante que não pode falhar, que cumpre o propósito da sua existência. Chamamos de caminho crítico (critical path) os passos que o usuário faz até chegar ao fluxo principal e executá-lo com sucesso.

No caso do projeto de exemplo, o caminho crítico é do login até o sucesso do pagamento, conforme demonstrado na Figura 1 do tópico anterior. Então execute o projeto e faça o teste manual do caminho crítico.

O próximo passo é automatizar o teste do caminho crítico, e o melhor caminho é com um teste de integração.

Teste de integração

O teste de integração (integration testing) é um nível de teste de software onde as unidades individuais são combinadas e testadas como um grupo. O método é de Caixa Preta (Black Box), que basicamente consiste em testar o projeto em execução sem se importar para sua estrutura interna, neste teste não importa se é um projeto React, apenas que é um projeto que iremos executá-lo em um navegador. A abordagem é o Big Bang, onde todas ou a maioria das unidades são combinadas e testadas de uma só vez.

O objetivo deste teste será garantir os fluxos da aplicação, principalmente o caminho crítico. Neste exemplo, o teste irá garantir que o usuário irá conseguir comprar um curso.

No passado a única solução era usar o Selenium, mas atualmente existem inúmeras opções tão boas quanto e até melhores. Neste exemplo usarei o Cypress, que é bem simples, fácil de usar e bem completo.

Na raiz do projeto, digite o seguinte comando no terminal:

cd client
npm install --save-dev cypress

Ele demora um pouco para instalar que as dependências npm convencionais, pois ele vai instalar uma aplicação desktop na sua máquina. Ele suporta MacOS, Linux e Windows.

Depois de instalado, ele vai ter criado o diretório /client/cypress/ com um conjunto de diretórios e arquivos de exemplo.

No arquivo package.json, dentro do scripts, inclua o script conforme exemplo abaixo:

Agora basta iniciá-lo com o comando dentro do /client:

npm run cypress:open

Irá abrir uma janela semelhante a essa:

Figura 2 — Cypress

Ele inicia com exemplos de testes, você pode removê-los apenas removendo o diretório examples dentro do diretório /client/cypress/integration.

Para continuar é importante que a API, o client e o Cypress estejam em execução. Para ter uma melhor experiência, se sua tela for grande ou você tiver usando dois monitores, mantenha a tela do Cypress aberta junto com o editor do código, você poderá ver o teste sendo executado toda vez que fizer uma alteração.

Crie o arquivo payment.spec.js no diretório /client/cypress/integration e inclua o seguinte código:

O arquivo criado deve aparecer no Cypress, clique duas vez no arquivo ou no botão “run all specs” para executar o teste, ele irá abrir o Chrome e irá executar o teste conforme imagem abaixo:

Figura 3 — Primeiro teste

Tão simples quanto foi criar o arquivo e executá-lo, é começar a testar a aplicação, podemos trocar o código anterior por um código que abre a aplicação e valida se existe o texto “Login” na tela:

O resultado é semelhante a imagem abaixo:

Figura 4 — Teste comcarregamento da aplicação

O objeto global cy fornece tudo que precisamos para o teste, e nele usamos o visit() com a URL da aplicação para carregá-la. O cy.contains("Login") buscou pelo texto “Login” na página para garantir que a página de login foi carregada.

Neste caso de teste, o usuário não possui uma conta, então ele terá que fazer seu cadastro, podemos reproduzir este passo assim:

No código acima, o click() foi utilizada para disparar o evento de clique no link. Para preencher o formulário, foi usado o get() para obter o elemento através de seu id e o type() para entrar com o texto. Para submeter o formulário poderia ser usado o contains() com uma string, mas o botão está com o texto todo em maiúsculo devido ao layout utilizado, então foi usado a expressão regular /cadastrar/i para ignorar a caixa do texto, mantendo a compatibilidade caso o layout mude. O resultado é este:

Figura 5 — Teste até a tela principal

Para terminar este teste, basta concluir o pagamento:

Como podemos ver no resultado final, com poucas linhas de código foi possível testar todo o caminho crítico:

Figura 6 — Teste do pagamento finalizado

Para garantir que o usuário possa voltar e fazer uma outra compra com o login já cadastrado, é preciso testar o login também. Sendo assim, crie o arquivo de teste auth.spec.js com o seguinte código:

Nesta primeira parte, foi abordado os seguintes passos:

  1. Entendimento e execução do projeto.
  2. Entendimento e teste manual dos fluxos da aplicação, os principais fluxos foram classificados como caminho crítico.
  3. Teste de integração no caminho crítico.

Agora existe uma garantia do estado atual do projeto legado, essa garantia dará um pouco de segurança para fazer qualquer modificação, mas este é só o começo.

Na próxima parte será abordado o começo da refatoração e sobre o código em React. Participe dando sua opinião sobre o que achou desta primeira parte, conte sua experiência com projetos legados, sobre testes integrados, ou qualquer assunto que queira comentar, seu feedback será muito importante para a construção dos próximos artigos.

--

--

Bruno Nardini
Bemobi
Writer for

Staff Software Engineer at Pipefy | Teacher NardiniAcademy.com | Blogger BrunoNardini.com | Husband & Father | Guitar Player | Build and teach the WEB