Testando projetos Node.js. Parte 2. Avaliação do desempenho do teste, integração contínua e análise da qualidade do código

Testando projetos Node.js. Parte 1. Anatomia e tipos de teste

Hoje, na segunda parte da tradução do material dedicado aos testes dos projetos Node.js., falaremos sobre avaliar a eficácia dos testes e analisar a qualidade do código.

imagem

Seção 3. Avaliação da eficácia dos testes


19. Alcance um nível suficientemente alto de cobertura de código com testes para ganhar confiança em sua operação correta. Geralmente, aproximadamente 80% de cobertura fornece bons resultados.


Recomendações


O objetivo do teste é garantir que o programador possa continuar trabalhando produtivamente no projeto, garantindo que o que já foi feito esteja correto. Obviamente, quanto maior o volume do código testado, mais forte a confiança de que tudo funciona como deveria. O indicador de cobertura de código por testes indica quantas linhas (ramificações, comandos) foram verificadas por testes. Qual deve ser esse indicador? É claro que 10 a 30% é muito pouco para dar confiança de que o projeto funcionará sem erros. Por outro lado, o desejo de 100% de cobertura do código com testes pode ser um prazer muito caro e pode distrair o desenvolvedor dos fragmentos mais importantes do programa, forçando-o a procurar no código aqueles lugares que os testes existentes não alcançam. Se você der uma resposta mais completa à pergunta sobre qual deve ser a cobertura do código com testes, podemos dizer que o indicador pelo qual devemos nos esforçar depende do aplicativo que está sendo desenvolvido. Por exemplo, se você estiver escrevendo um software para a próxima geração do Airbus A380, 100% é um indicador que nem é discutido. Mas se você criar um site no qual as galerias de caricaturas serão exibidas, provavelmente 50% já é demais. Embora os especialistas em testes digam que o nível de cobertura do código com os testes que você deve buscar depende do projeto, muitos deles mencionam os 80%, o que provavelmente é adequado para a maioria dos aplicativos. Por exemplo, aqui estamos falando de algo na região de 80 a 90% e, de acordo com o autor deste material, 100% de cobertura do código com testes o deixa desconfiado, pois pode indicar que o programador escreve testes apenas para obter número bonito no relatório.

Para usar os indicadores dos testes de cobertura de código, você precisará configurar corretamente seu sistema para integração contínua (IC, integração contínua). Isso permitirá, se o indicador correspondente não atingir um determinado limite, interromper a montagem do projeto. Veja como configurar o Jest para coletar informações de cobertura de teste. Além disso, você pode configurar os limites de cobertura não para todo o código, mas focando em componentes individuais. Além disso, considere detectar uma diminuição na cobertura do teste. Isso acontece, por exemplo, ao adicionar novo código a um projeto. O controle desse indicador incentivará os desenvolvedores a aumentar o volume do código testado ou, pelo menos, manter esse volume no nível existente. Em vista do exposto, a cobertura do código com testes é apenas um indicador quantificado, o que não é suficiente para avaliar completamente a confiabilidade dos testes. Além disso, como será mostrado abaixo, seus altos níveis ainda não significam que o código “coberto com testes” esteja realmente verificado.

Consequências do desvio das recomendações


A confiança do programador na alta qualidade do código e indicadores relacionados relacionados ao teste andam de mãos dadas. Um programador não pode deixar de ter medo de erros se não souber que a maior parte do código de seu projeto é coberta por testes. Essas preocupações podem retardar o seu projeto.

Exemplo


Veja como é um relatório típico de cobertura de teste.


Relatório de cobertura de teste gerado por Istambul

Abordagem correta


Aqui está um exemplo de configuração do nível desejado de cobertura de teste do código do componente e o nível geral deste indicador no Jest.


Definindo o nível desejado de cobertura de código com testes para todo o projeto e para um componente específico

▍20. Examine os relatórios sobre a cobertura do código com testes para identificar partes desprotegidas do código e outras anomalias


Recomendações


Alguns problemas tendem a passar por uma variedade de sistemas de detecção de erros. Pode ser difícil detectar essas coisas usando ferramentas tradicionais. Talvez isso não se aplique a erros reais. Em vez disso, estamos falando sobre um comportamento inesperado do aplicativo, que pode ter consequências devastadoras. Por exemplo, muitas vezes acontece que alguns fragmentos de código nunca são usados ​​ou raramente são chamados. Por exemplo, você acha que os mecanismos da classe PricingCalculator sempre PricingCalculator usados ​​para definir o preço de um produto, mas, na verdade, essa classe não é usada, e há registros de 10.000 produtos no banco de dados e na loja online em que o sistema é usado, muitas vendas ... Relatórios sobre cobertura de código com testes ajudam o desenvolvedor a entender se o aplicativo funciona da maneira que deve funcionar. Além disso, nos relatórios, você pode descobrir qual código de projeto não foi testado. Se você se concentrar em um indicador geral que indique que os testes cobrem 80% do código, não poderá descobrir se partes críticas do aplicativo estão sendo testadas. Para gerar esse relatório, basta configurar corretamente a ferramenta usada para executar os testes. Esses relatórios geralmente parecem bastante bonitos e sua análise, que não leva muito tempo, permite detectar todo tipo de surpresa.

Consequências do desvio das recomendações


Se você não sabe quais partes do seu código não foram testadas, não sabe onde pode esperar problemas.

Abordagem incorreta


Veja o próximo relatório e pense no que lhe parece incomum.


Relatório indicando comportamento incomum do sistema

O relatório é baseado em um cenário real de uso de aplicativos e permite que você veja um comportamento incomum do programa associado aos usuários que efetuam logon no sistema. Ou seja, um número inesperadamente grande de tentativas malsucedidas de entrar no sistema chama sua atenção em comparação com as bem-sucedidas. Após analisar o projeto, verificou-se que o motivo era um erro no frontend, devido ao qual a parte da interface do projeto enviava constantemente solicitações correspondentes à API do servidor para a entrada no sistema.

21. Avalie a cobertura do código lógico com testes usando teste de mutação


Recomendações


As métricas tradicionais de benchmarking podem não ser confiáveis. Portanto, no relatório, pode haver um valor de 100%, mas, ao mesmo tempo, absolutamente todas as funções do projeto retornarão valores incorretos. Como explicar isso? O fato é que o indicador de cobertura de código pelos testes indica apenas quais linhas de código foram executadas sob o controle do sistema de teste, mas não depende se algo foi realmente verificado, ou seja, se as instruções do teste foram visando verificar a exatidão dos resultados do código. É semelhante a uma pessoa que, retornando de uma viagem de negócios ao exterior, mostra carimbos no passaporte. Os selos provam que ele foi a algum lugar, mas eles não dizem nada sobre se ele fez o que fez em viagem de negócios.

Aqui, os testes de mutação podem nos ajudar, o que nos permite descobrir quanto código foi realmente testado, e não apenas visitado pelo sistema de teste. Para teste mutacional, você pode usar a biblioteca Stryker JS. Aqui estão os princípios pelos quais ele funciona:

  1. Ela intencionalmente altera o código, criando erros nele. Por exemplo, o código newOrder.price===0 transforma em newOrder.price!=0 . Esses "erros" são chamados de mutações.
  2. Ela faz testes. Se eles acabam sendo aprovados, então temos problemas, porque os testes não cumprem sua tarefa de detectar erros e os "mutantes", como dizem, "sobrevivem". Se os testes indicarem erros no código, tudo estará em ordem - os "mutantes" "morrem".

Se acontecer que todos os "mutantes" foram "mortos" (ou, pelo menos, a maioria deles não sobreviveu), isso proporciona um nível mais alto de confiança na alta qualidade do código e nos testes que o testam do que as métricas tradicionais para cobrir o código com testes. Ao mesmo tempo, o tempo necessário para configurar e realizar testes mutacionais é comparável ao necessário ao usar testes convencionais.

Consequências do desvio das recomendações


Se o indicador tradicional de cobertura de código por testes indicar que 85% do código é coberto por testes, isso não significa que os testes possam detectar erros nesse código.

Abordagem incorreta


Aqui está um exemplo de 100% de cobertura do código com testes, nos quais o código não foi completamente testado.

 function addNewOrder(newOrder) {   logger.log(`Adding new order ${newOrder}`);   DB.save(newOrder);   Mailer.sendMail(newOrder.assignee, `A new order was places ${newOrder}`);   return {approved: true}; } it("Test addNewOrder, don't use such test names", () => {   addNewOrder({asignee: "John@mailer.com",price: 120}); });//    100%, ,   ,     

Abordagem correta


Aqui está o relatório de teste de mutação gerado pela biblioteca Stryker. Ele permite que você descubra quanto código não foi testado (isso é indicado pelo número de "mutantes" "sobreviventes").


Relatório Stryker

Os resultados deste relatório permitem, com mais confiança do que os indicadores usuais de cobertura de código com testes, dizer que os testes funcionam conforme o esperado.

  • Uma mutação é um código que foi deliberadamente modificado pela biblioteca Stryker para testar a eficácia de um teste.
  • O número de "mutantes" "mortos" (mortos) mostra o número de defeitos de código criados intencionalmente ("mutantes") que foram identificados durante o teste.
  • O número de "sobreviventes" "mutantes" (sobreviventes) permite descobrir quantos testes de defeitos de código não foram encontrados.

Seção 4. Integração Contínua, Outros Indicadores de Qualidade de Código


22. Aproveite os recursos do linter e interrompa o processo de construção do projeto quando ele detectar problemas que eles relatam


Recomendações


Hoje, os linters são ferramentas poderosas que podem identificar problemas sérios de código. É recomendável, além de algumas regras básicas de linting (como as implementadas pelos plugins eslint-plugin-standard e eslint-config-airbnb ), usar regras especializadas. Por exemplo, estas são as regras implementadas por meio do plugin eslint-plugin-chai-expect para verificar a correção do código de teste, essas são as regras do plugin eslint-plugin-promessa que controlam o trabalho com promessas, essas são as regras do eslint-plugin-security que verificam o código quanto à disponibilidade contém expressões regulares perigosas. Aqui também é possível mencionar o plug-in eslint-plugin-you-not-need-lodash-underscore , que permite encontrar no código o uso de métodos de bibliotecas externas que possuem análogos em JavaScript puro.

Consequências do desvio das recomendações


Chegou um dia chuvoso, o projeto apresenta falhas contínuas na produção e não há informações sobre pilhas de erros nos logs. O que aconteceu Como se viu, o que o código lança como exceção não é realmente um objeto de erro. Como resultado, informações sobre a pilha não entram nos logs. De fato, em tal situação, o programador pode matar contra a parede ou, o que é muito melhor, passar 5 minutos configurando o linter, o que detectará facilmente o problema e garantirá ao projeto problemas semelhantes que possam surgir no futuro.

Abordagem incorreta


Aqui está o código que, inadvertidamente, lança um objeto comum como uma exceção, enquanto aqui você precisa de um objeto do tipo Error . Caso contrário, os dados sobre a pilha não entrarão no log. O ESLint encontra o que poderia causar problemas de produção, ajudando a evitar esses problemas.


ESLint ajuda a encontrar um bug no seu código

23. Feedback mais rápido para desenvolvedores que usam integração contínua local


Recomendações


Usando um sistema centralizado de integração contínua, que ajuda a controlar a qualidade do código, testando-o, usando o linter, verificando se há vulnerabilidades? Nesse caso, verifique se os desenvolvedores podem executar este sistema localmente. Isso permitirá que eles verifiquem instantaneamente seu código, o que acelera o feedback e reduz o tempo de desenvolvimento do projeto. Por que isso é assim? Um processo eficaz de desenvolvimento e teste envolve muitas operações repetidas ciclicamente. O código é testado, o desenvolvedor recebe um relatório e, se necessário, o código é refatorado, após o que tudo é repetido. Quanto mais rápido o ciclo de feedback funciona, mais rapidamente os desenvolvedores recebem relatórios sobre o teste de código, mais iterações de aprimoramento desse código podem ser executadas. Se demorar muito tempo para obter um relatório de teste, isso pode levar à baixa qualidade do código. Digamos que alguém estava trabalhando em algum módulo, depois começou a trabalhar em outra coisa e, em seguida, recebeu um relatório sobre o módulo, indicando que o módulo precisa ser aprimorado. No entanto, já ocupado com assuntos completamente diferentes, o desenvolvedor não prestará atenção suficiente ao módulo do problema.

Alguns provedores de soluções de IC (digamos que isso se aplica ao CircleCI ) permitem executar o pipeline de CI localmente. Algumas ferramentas pagas, como Wallaby.js (o autor observa que ele não está conectado a este projeto), podem obter rapidamente informações valiosas sobre a qualidade do código. Além disso, o desenvolvedor pode simplesmente adicionar o script npm apropriado ao package.json , que executa verificações de qualidade de código (testa, analisa com um linter, procura por vulnerabilidades) e até usa o pacote simultâneo para acelerar as verificações. Agora, para verificar o código de forma abrangente, será suficiente executar um único comando, como npm run quality , e obter imediatamente um relatório. Além disso, se os testes de código indicarem que há problemas, você poderá cancelar as confirmações usando git hooks (a biblioteca husky pode ser útil para resolver esse problema).

Consequências do desvio das recomendações


Se um desenvolvedor receber um relatório sobre a qualidade do código um dia após escrever esse código, é provável que esse relatório se transforme em um documento formal, e os testes de código serão separados do trabalho, não se tornando sua parte natural.

Abordagem correta


Aqui está um script npm que verifica a qualidade do código. A realização de verificações é paralelizada. O script é executado ao tentar enviar novo código para o repositório. Além disso, o desenvolvedor pode iniciá-lo por sua própria iniciativa.

 "scripts": {   "inspect:sanity-testing": "mocha **/**--test.js --grep \"sanity\"",   "inspect:lint": "eslint .",   "inspect:vulnerabilities": "npm audit",   "inspect:license": "license-checker --failOn GPLv2",   "inspect:complexity": "plato .",     "inspect:all": "concurrently -c \"bgBlue.bold,bgMagenta.bold,yellow\" \"npm:inspect:quick-testing\" \"npm:inspect:lint\" \"npm:inspect:vulnerabilities\" \"npm:inspect:license\"" }, "husky": {   "hooks": {     "precommit": "npm run inspect:all",     "prepush": "npm run inspect:all"   } } 

§24. Realize testes de ponta a ponta em um espelho realista do ambiente de produção


Recomendações


No vasto ecossistema de Kubernetes, ainda existe um consenso em usar ferramentas adequadas para implantar ambientes locais, embora essas ferramentas apareçam com bastante frequência. Uma abordagem possível aqui é executar um Kubernetes “minimizado” usando ferramentas como Minikube ou MicroK8s , que permitem criar ambientes leves que se assemelham aos reais. Outra abordagem é testar projetos em um ambiente Kubernetes "real" remoto. Alguns provedores de IC (como o Codefresh ) possibilitam a interação com os ambientes internos do Kubernetes, o que simplifica o trabalho dos pipelines de CI ao testar projetos do mundo real. Outros permitem que você trabalhe com ambientes remotos do Kubernetes.

Consequências do desvio das recomendações


O uso de várias tecnologias na produção e teste requer o suporte de dois modelos de desenvolvimento e leva à separação de equipes de programadores e especialistas em DevOps.

Abordagem correta


Aqui está um exemplo de uma cadeia de ICs, que, como dizem, em tempo real cria um cluster Kubernetes (isso é obtido aqui ).

 deploy: stage: deploy image: registry.gitlab.com/gitlab-examples/kubernetes-deploy script: - ./configureCluster.sh $KUBE_CA_PEM_FILE $KUBE_URL $KUBE_TOKEN - kubectl create ns $NAMESPACE - kubectl create secret -n $NAMESPACE docker-registry gitlab-registry --docker-server="$CI_REGISTRY" --docker-username="$CI_REGISTRY_USER" --docker-password="$CI_REGISTRY_PASSWORD" --docker-email="$GITLAB_USER_EMAIL" - mkdir .generated - echo "$CI_BUILD_REF_NAME-$CI_BUILD_REF" - sed -e "s/TAG/$CI_BUILD_REF_NAME-$CI_BUILD_REF/g" templates/deals.yaml | tee ".generated/deals.yaml" - kubectl apply --namespace $NAMESPACE -f .generated/deals.yaml - kubectl apply --namespace $NAMESPACE -f templates/my-sock-shop.yaml environment: name: test-for-ci 

▍25. Esforce-se para paralelizar a execução do teste


Recomendações


Se o sistema de teste estiver bem organizado, ele se tornará seu amigo fiel, 24 horas por dia, pronto para relatar problemas com o código. Para fazer isso, os testes devem ser realizados muito rapidamente. Na prática, verifica-se que executar 500 testes de unidade no modo de thread único que usam o processador intensivamente leva muito tempo. E esses testes precisam ser realizados com bastante frequência. Felizmente, ferramentas modernas para executar testes ( Jest , AVA , uma extensão para Mocha ) e plataformas de CI podem executar testes em paralelo usando vários processos, o que pode melhorar significativamente a velocidade de recebimento de relatórios de teste. Algumas plataformas de IC sabem paralelizar testes entre contêineres, o que melhora ainda mais o ciclo de feedback. Para paralelizar com êxito a execução de testes, local ou remoto, os testes não devem depender um do outro. Testes independentes podem ser executados em diferentes processos sem problemas.

Consequências do desvio das recomendações


Obter resultados do teste uma hora depois de enviar o código ao repositório enquanto trabalha nos novos recursos do projeto é uma ótima maneira de reduzir a utilidade dos resultados do teste.

Abordagem correta


Graças à execução paralela de testes, a biblioteca mocha-parallel-test e a estrutura Jest ignoram facilmente o Mocha ( essa é a fonte dessa informação).


Ferramentas de teste de desempenho de teste

§26. Proteja-se de questões legais usando a verificação de licença e verificação de código de plágio


Recomendações


Talvez agora você não esteja particularmente preocupado com problemas com a lei e plágio. No entanto, por que não verificar seu projeto quanto a problemas semelhantes? Existem muitas ferramentas disponíveis para organizar essas inspeções. Por exemplo, são verificadores de licença e verificadores de plágio (este é um pacote comercial, mas existe a possibilidade de seu uso gratuito). É fácil integrar essas verificações ao pipeline do IC e verificar o projeto, por exemplo, a presença de dependências com licenças limitadas ou a presença de código copiado do StackOverflow e provavelmente infringindo os direitos autorais de outra pessoa.

Consequências do desvio das recomendações


O desenvolvedor, inadvertidamente, pode usar o pacote com uma licença que não é adequada ao seu projeto ou copiar o código comercial, o que pode levar a problemas legais.

Abordagem correta


Instale o pacote do verificador de licença localmente ou em um ambiente de IC:

 npm install -g license-checker 

Verificaremos as licenças com ele e, se ele encontrar algo que não nos convém, reconheceremos a verificação como malsucedida. O sistema de CI, ao detectar que algo deu errado durante a verificação das licenças, interromperá a montagem do projeto.

 license-checker --summary --failOn BSD 


Verificação de licença

27. Verifique constantemente o projeto em busca de dependências vulneráveis


Recomendações


Até pacotes altamente respeitados e confiáveis, como o Express, têm vulnerabilidades. Para identificar essas vulnerabilidades, você pode usar ferramentas especiais - como a ferramenta padrão para auditar pacotes npm ou o projeto snyk comercial, que possui uma versão gratuita. Essas verificações, juntamente com outras, podem fazer parte do pipeline do IC.

Consequências do desvio das recomendações


Para proteger seu projeto contra vulnerabilidades de suas dependências sem usar ferramentas especiais, você precisará monitorar constantemente as publicações sobre essas vulnerabilidades. Esta é uma tarefa que consome muito tempo.

Abordagem correta


Aqui estão os resultados da verificação do projeto usando o NPM Audit.


Relatório de pacote de verificação de vulnerabilidade

28. Automatize atualizações de dependência


Recomendações


O caminho para o inferno é pavimentado com boas intenções. Essa ideia é totalmente aplicável ao package-lock.json , cuja utilização, por padrão, bloqueia as atualizações do pacote. Isso acontece mesmo nos casos em que os projetos são trazidos a um estado npm install npm update comandos npm install e npm update . Isso leva, na melhor das hipóteses, ao uso de pacotes desatualizados ou, na pior, à aparência de código vulnerável no projeto. As equipes de desenvolvimento, como resultado, dependem da atualização manual de informações sobre versões adequadas de pacotes ou de utilitários como ncu , que, novamente, são iniciados manualmente. O processo de atualização de dependências é melhor automatizado, concentrando-se no uso das versões mais confiáveis ​​dos pacotes usados ​​no projeto. Essa não é a única solução correta, no entanto, ao automatizar as atualizações de pacotes, existem algumas abordagens dignas de nota. O primeiro é injetar algo como verificar pacotes usando npm-outdated ou npm-check-updates (ncu) no pipeline do IC. Isso ajudará a identificar pacotes obsoletos e incentivará os desenvolvedores a atualizá-los. A segunda abordagem é usar ferramentas comerciais que verificam o código e fazem automaticamente solicitações pull destinadas a atualizar dependências. No campo da atualização automática de dependências, somos confrontados com outra questão interessante sobre a política de atualização. Se atualizada com cada novo patch, a atualização pode colocar muito estresse no sistema. Se você atualizar imediatamente após o lançamento da próxima versão principal do pacote, isso poderá levar ao uso de soluções instáveis ​​no projeto (vulnerabilidades em muitos pacotes são encontradas exatamente nos primeiros dias após o lançamento, leia sobre o incidente com o escopo do eslint). « », , . , 1.3.1, 1.3.2, 1.3.8.


, , , .


ncu , , , .


ncu

▍29. , Node.js



, Node.js-, , Node.js .

  1. . — , , , Jenkins .
  2. , Docker.
  3. . , , . (, ), , , , .
  4. , , , . — , , , .
  5. , . , feature, — master, , ( ).
  6. . , .
  7. .
  8. (, Docker) .
  9. , , , . , node_modules .


, , .

▍30.



, . , , , Node.js , . CI-, , « ». , , , . , , mySQL, — Postgres. , Node.js, — 8, 9 10. , . CI-.


, , , . , , .


CI- Travis Node.js.

 language: node_js node_js: - "7" - "6" - "5" - "4" install: - npm install script: - npm run test 

Sumário


, , . , , .

Caros leitores! ?

Source: https://habr.com/ru/post/pt435464/


All Articles