Alura > Cursos de Programação > Cursos de Node.JS > Conteúdos de Node.JS > Primeiras aulas do curso Node.js: testes unitários e de integração

Node.js: testes unitários e de integração

Conceituando testes - Apresentação

Olá. Meu nome é João Manoel e eu serei o seu instrutor nesse curso de testes em Node.js.

Nesse curso, veremos alguns conceitos e fundamentos de testes em aplicações e em software; vamos entender o que é uma cultura de testes e talvez um pouco de como podemos começar a implantá-la.

Vamos entender o que são testes estáticos e testes unitários e fazer vários exemplos de exercícios com eles. Vamos conhecer as asserções, seu significado e importância. Vamos utilizar vários exemplos e demonstrações para implementar testes em frameworks como o Jest. E também faremos testes de integração utilizando um framework chamado SuperTest.

E para quem é esse curso? Se você nunca teve contato com testes de aplicações e quer aprender a fazer os testes unitários em Node.js, esse curso é para você; ou se você já conhece alguns conceitos de testes em outras ferramentas e quer ver implementações que sejam específicas em JavaScript no Node.js.

E para quem não é esse curso? Se você já é um profissional que está ambientado com testes no seu cotidiano, você já os domina, consegue escrevê-los e entendê-los, eu acho que não vale muito a pena, você já terá o conteúdo que será demonstrado no curso.

Ou se você já conhece bem os conceitos de teste aplicado em Node.js, se você já utiliza esses frameworks há muito tempo e os conhece bem, também pode não ser um bom investimento do seu tempo.

E temos alguns requisitos nesse curso. Seria necessário você já entender os fundamentos do JavaScript, pois já vamos construir em cima dessa base e é necessário que você tenha um entendimento.

E na segunda parte, iremos utilizar uma API que já foi feita em outros cursos e projetos que já estão parcialmente feitos. Então é interessante que você saiba como uma API feita em Node, especificamente com o framework Express, funcione, ou que pelo menos você já tenha uma noção e tenha feito alguns cursos ou já tenha estudado esses tipos de APIs.

Então se você se encaixa nesse público alvo e gostaria de aprender um pouco mais, te espero no próximo vídeo.

Conceituando testes - Tipos de testes

Nessa aula, trataremos um pouco sobre a fundamentação dos testes. Daremos uma motivação sobre por que deveríamos utilizar testes enquanto desenvolvemos e também mencionar alguns benefícios de ter uma cultura de testes dentro do nosso ecossistema e das ferramentas que utilizamos.

Se você já desenvolveu qualquer código em geral ou trabalhou em um projeto, já deve ter se deparado com um erro que aconteceu durante a execução do seu programa e o fluxo de execução foi interrompido inesperadamente.

Imagine as consequências de uma interrupção num cenário em que você tem um servidor que está rodando esse programa com sua equipe, com esse projeto que você desenvolve, e tem pessoas que dependem daquele sistema, existem diversos usuários que utilizam aquilo.

É interessante que não aconteça esse problema de interrupção da execução, que tenhamos que subir ou descobrir o que aconteceu para consertar enquanto as pessoas estão utilizando e realmente dependem daquilo para qualquer que seja a funcionalidade.

Então, seria interessante poder evitar que esses erros aconteçam durante o que chamamos de run time, ou seja, durante esse tempo de execução, e também em sistemas que chamamos de sistemas de produção, que é um sistema que realmente está atendendo a demanda das pessoas e resolvendo um problema em tempo real naquele momento.

Então voltamos para pensar um pouco: por que erros acontecem? Como podemos evitar que eles cheguem até essas máquinas também que estão resolvendo esse problema e que estão em funcionamento? E como fazemos para lidar com esse fluxo de consertar ou manter alguma coisa funcionando?

A resposta para a primeira pergunta é que são os seres humanos, pelo menos até agora, que desenvolvem os códigos. E, como seres humanos, temos tendências a falhar. É a velha máxima de que nenhum ser humano é perfeito.

Muitos enganos podem acontecer, nem todos resultam em um erro de uma aplicação que cairá. Pode ser só um desenvolvedor que não entendeu plenamente certa funcionalidade, ou não foi passado direito para ele como deveria ser processada uma informação, ou até um serviço que roda, mas não necessariamente entrega uma resposta verdadeira no final para quem está utilizando aquele sistema.

E como os testes vão nos ajudar a ver o que está acontecendo na aplicação e evitar esses problemas que foram mencionados? Afinal, quem vai escrever o código também é um humano, então não estamos só mexendo o objetivo, empurrando o problema para frente?

Em parte, está certo. Os testes serão escritos majoritariamente por seres humanos ainda, e existe a propensão de testes conterem erros. Porém, temos que pensar um pouco mais acima e ver que, ao implementar um sistema de testes, teremos vários procedimentos em volta de como vamos lidar com esses sistemas e informações, e eles serão mais um mecanismo na frente de termos um problema em um sistema de produção.

Então, é um esforço que faremos coletivamente para pensar em como mitigar esses problemas. Não é uma solução que vai resolver tudo, mas é uma área de estudo que visa evitar que eles resultem em tantos danos quanto se não tivesse nenhum sistema naquele lugar.

Alguns outros benefícios que veremos de utilizar testes não é só resolver problemas críticos e evitar que esses grandes eventos aconteçam. Eles também podem aumentar a produtividade do ambiente de desenvolvimento e também aumentam a confiabilidade que as pessoas têm naquele sistema. Apesar de não ser algo muito material, você terá uma paz enquanto você desenvolve aquilo e toda a equipe poderá contribuir e colaborar de uma forma mais tranquila.

Hoje em dia, para nos auxiliar na estruturação de como é testar esses sistemas, temos uma metáfora chamada pirâmide de testes. Ela muito comumente é composta por três partes representadas da seguinte forma: temos os testes unitários na base da pirâmide, na maior parte, que são os menores componentes da nossa base de código, que serão testados individualmente, e por isso o nome “teste unitário”.

Acima deles, no meio da pirâmide, temos os testes de integração, onde são testadas as integrações entre as partes do projeto e algumas dependências externas, e nós veremos um pouco mais sobre isso.

E no topo da pirâmide, na menor parte, temos os chamados testes End-to-End, representados na sigla E2E que vêm do inglês, e que é o teste da aplicação de ponta a ponta. Esse é o significado do End-to-End: é o teste de início ao fim, que vê tudo que está acontecendo na aplicação.

Pirâmide de testes, dividida em três segmentos horizontais. Da base até o topo, são eles: "Unitários", "Integração" e "E2E".

Não precisamos nos prender muito a essa nomenclatura específica, porque elas vão variar de acordo com a literatura que você está olhando, e pode depender até das ferramentas que você vai utilizar para testar a sua aplicação. Então, estamos usando o Node, veremos algumas ferramentas do mundo do JavaScript e eu escolhi essa nomenclatura para representar esse ambiente, mas elas podem variar se você utilizar outras ferramentas, linguagens de programação ou ambientes.

Vamos tomar essa pirâmide então e eu vou explicar um pouco mais sobre cada uma dessas camadas.

Falando agora um pouco mais sobre os testes unitários: é um teste interessante, a responsabilidade dele será analisar pequenas frações do código, e geralmente essas frações podem ser simplesmente uma função ou método isolado. Eles não dependem da estrutura completa da aplicação, então só com um pedaço do projeto você já pode começar a implementar esses testes e ver se aquele pedaço está se comportando da forma como deveria.

A importância desse teste é justamente essa: poder verificar se a especificação que demos ao projeto, a uma função, método ou classe está sendo seguida e está produzindo aquele resultado que nós esperamos.

Em um exemplo prático, vamos imaginar um sistema de crédito, como um banco ou uma agência. Queremos garantir que as taxas estejam sendo cobradas do usuário, do cliente final. Então, podemos criar testes unitários que vão verificar as funções que calculam essas taxas. Ela verá se o valor que está entrando e saindo corresponde ao valor que eu dei como entrada e que esperava que saísse daquela função.

Então os testes unitários têm uma maior granularidade, como falamos. Eles são componentes bem pequenos em relação ao projeto inteiro e também podem ser testados de forma isolada, separadamente.

Só que como nada é perfeito, ter todas essas unidades pequenas funcionando sozinhas não garante que a integração delas vai funcionar. É como se a soma das partes não desse o inteiro, nós não podemos assumir isso.

Então, para essa integração, nós veremos os chamados testes de integração, ou testes de serviços. Eles serão responsáveis por considerar também as interfaces entre cada um daqueles pedaços da aplicação.

Quando você faz uma chamada de um módulo para outro, ou você precisa acessar uma chamada externa, um banco de dados ou alguma API, algo que não seja seu, você terá que fazer alguns testes que vão simular e permitirão ver se o comportamento que você programou está de acordo com aquilo que você espera.

Um exemplo disso, falando sobre as APIs, é que podemos considerar essa etapa quando já temos ou estamos implementando alguma rota ou recurso para fazer uma requisição. Podemos subir uma API ou um pedaço dela e fazer algumas chamadas para ver se a resposta dela está sendo adequada.

Esses tipos de teste conferem se os módulos menores do projeto estão funcionando em conjunto, só que ter os módulos e as integrações funcionando em conjunto também não engloba todo o projeto, porque essa parte ficará delegada aos testes End-to-End, os testes de ponta a ponta.

O teste End-to-End é um teste que vai analisar o fluxo completo da nossa aplicação, então ele passará por todos os módulos e stacks, mas não diretamente. Por exemplo, no caso de um sistema web, chamaremos esse teste de alto nível, ou seja, vamos utilizar algum programa que clique na interface, que faça cadastros, que navegue entre as páginas que existem e consiga observar as respostas daquela página e do uso, como se fosse uma pessoa mesmo, e dizer se aquilo está se comportando da forma que esperamos, porque afinal estamos testando e queremos que ele siga um padrão, uma especificação determinada.

Então, ele não vai observar muito o mérito da implementação em si do projeto. Ele verá se como um todo está funcionando e vai testar as funcionalidades que utilizamos e propomos ao usuário final.

Essa etapa acaba sendo uma das mais custosas para utilizar, porque para chegar ao teste End-to-End você já deve ter boa parte da aplicação funcionando, ou pelo menos deve ter um pedaço de cada área da aplicação funcionando para que você possa testá-la como um todo.

Nesse curso nós não abordaremos muito essa etapa. Vamos nos concentrar mais nos testes voltados para o back-end, então vamos lidar mais com os testes do dia a dia, como os testes unitários e um pouco dos testes de integração.

Na plataforma, deixaremos alguns conteúdos que vão apontar para outros ambientes que incluem os testes End-to-End nos materiais complementares.

A questão da pirâmide também reflete um dado, porque podemos subir ou descer na pirâmide e isso ficará ligado diretamente a alguns conceitos. Quanto mais abaixo estivermos na pirâmide, mais isolados os testes serão, bem como os componentes que estamos analisando, e maior será o volume de testes, porque ele vai depender da implementação.

Então, cada método e função que existir pode ser testado de forma isolada como uma unidade. E eles são relativamente mais simples comparados ao resto da pirâmide. Você pode pegar um arquivo, começar a escrever e ter alguns testes que vão validar o que você está fazendo.

E conforme vamos subindo na pirâmide, esses testes requerem maior coordenação e integração entre as partes das tecnologias. Eles consomem mais recursos, levam mais tempo e mais hora de obra, hora de pessoas dentro da aplicação, hora de desenvolvimento. E eles são mais complexos, inevitavelmente, porque eles vão levar em consideração muitos aspectos do desenvolvimento.

Então vamos agora descobrir um pouco mais sobre como podemos começar a trabalhar com os testes em si no Node.js.

Conceituando testes - Cultura de testes

Para obtermos todos esses benefícios que foram explorados no vídeo anterior, precisaremos implementar e ter uma certa organização na hora de implementar e adotar esse sistema. Para se referir a esse ambiente de utilizar testes durante o desenvolvimento, já existe e nós vamos utilizar um termo chamado “cultura de testes”. Eu vou explicar um pouco mais sobre o que seria ter uma cultura de testes ou como desenvolver uma.

Ter uma cultura de testes significa criar um ambiente onde a equipe de desenvolvimento tenha a capacidade de implementar e gerir os testes, entendendo como esses testes afetam a qualidade do código e permitindo que problemas que eventualmente passem desse ambiente de testes sejam resolvidos. No geral, podemos conceituar como utilizar boas práticas de testes dentro da nossa companhia, organização ou projeto.

Veremos mais a fundo agora algumas etapas e processos que podemos implementar de forma prática, para adotar a cultura de testes.

Para ter uma cultura de testes, vamos considerar três fatores fundamentais: o primeiro fator será a qualidade. A qualidade é ter uma métrica para saber se algo é bom ou ruim. As métricas serão regras que vamos determinar, e nós criaremos objetivos para nossa base de códigos e observaremos conforme estamos chegando perto desses objetivos ou se estamos nos afastando deles.

O segundo fator que vamos considerar é a confiança, que é algo mais subjetivo. Se todo mundo está programando e considerando a qualidade do que está sendo desenvolvido e buscando atingir aquelas métricas que foram ajustadas, nós teremos uma maior confiança na qualidade e no entendimento da nossa base de código, e continuaremos em operação. Nós vamos reduzir os custos e seguir de forma eficiente.

E em terceiro lugar, mas não menos importante, é o tempo, um fator muito importante, na verdade. Quanto mais economizarmos no tempo, podemos adotar e já pensar em outras funcionalidades para atingir os objetivos de negócio, que são a razão pela qual estamos desenvolvendo o produto.

Temos que mentalizar que investir em testes, resolver os bugs atuais, criar testes e refatorar o código é uma economia e um investimento de tempo, porque se deixarmos ou menosprezarmos aquilo que está acontecendo e já está na base de código atual, vamos gastar muito mais tempo no futuro para adicionar outras funcionalidades ou até mesmo uma hora de manter a aplicação no ar em si.

Eu vou mostrar algumas etapas que podemos seguir para atingir esses fatores fundamentais que eu mencionei. Vamos separar esse processo em algumas etapas. A primeira delas é a análise de requisitos, a segunda será o plano de teste, a terceira o caso de teste, a quarta o ambiente de teste e a quinta será a implementação.

Na análise de requisitos, vamos identificar quais funcionalidades estarão presentes no projeto e selecionar quais testes e quais tipos de teste vamos implementar para poder atingir esses objetivos.

No plano de teste, comumente, o time conhecido como QA, que é o Quality Assurance, ou os analistas de qualidade, elaboram o plano de teste, contendo as ferramentas que serão utilizadas, dividindo as responsabilidades de quem vai criar os testes e estimando no geral qual será o tempo, a complexidade e os gastos de recursos que terá naquele projeto.

No caso de teste são detalhados os testes em si: quais são as condições, os dados de entrada, os comportamentos esperados, dados de saída, quantidade de testes. Todo esse mapeamento é feito nos casos de teste.

No ambiente de teste são escolhidos onde e como esses testes serão executados. É feito o pipeline, o fluxo de como é produzido pela equipe de desenvolvimento e como aquilo vai sendo testado, ferramentas de versionamento e tudo que será utilizado, onde as alterações e implementações que são feitas pelo time de desenvolvimento vão sendo testadas e validadas para poder seguir no projeto.

Eles podem ser testes que rodam automaticamente, idealmente; podem ser pessoas que serão responsáveis por verificar, criação de tickets, enfim. Isso vai depender de cada instituição e das plataformas e ferramentas que elas conhecem.

Temos depois a implementação, onde é feita a documentação daqueles resultados que foram obtidos com os testes, problemas que aconteceram dentro dos processos, estabelecendo como podem ter melhorias para os próximos ciclos e toda essa parte que vai lidar diretamente com a implementação, tanto do código em si do projeto quanto da implementação dos testes e tudo que aconteceu em torno disso.

É importante ressaltar que o time, as pessoas que desenvolvem, tanto os QA, desenvolvedores, quanto quem gerencia e cria os requisitos precisam se comunicar muito bem durante todas essas etapas, porque as demandas que saem de um e de outro vão afetar diretamente qual será o resultado daquele projeto. E ter relatórios de cada uma dessas fases pode auxiliar na discussão para propor melhorias, reduzir as falhas, reduzir gastos operacionais, enfim. Ter isso documentado é de grande importância.

E agora eu falarei um pouco sobre os chamados testes estáticos. Eu não os incluí necessariamente nas partes anteriores porque eles podem depender e variar bastante da ferramenta que você está utilizando, da linguagem de programação ou do ambiente onde você executa o código. Então se liga no próximo vídeo que veremos um pouco sobre isso e começaremos a desenvolver nosso código.

Sobre o curso Node.js: testes unitários e de integração

O curso Node.js: testes unitários e de integração possui 240 minutos de vídeos, em um total de 60 atividades. Gostou? Conheça nossos outros cursos de Node.JS em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Node.JS acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas