[00:00] 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.
[00:21] 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.
[00:38] 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.
[00:54] É 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.
[01:10] 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.
[01:31] 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?
[01:48] 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.
[02:03] 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.
[02:28] 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?
[02:46] 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.
[03:16] 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.
[03:37] 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.
[04:09] 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”.
[04:38] 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.
[04:49] 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.
[05:10] 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.
[05:37] Vamos tomar essa pirâmide então e eu vou explicar um pouco mais sobre cada uma dessas camadas.
[05:42] 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.
[06:11] 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.
[06:24] 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.
[06:52] 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.
[07:04] 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.
[07:19] 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.
[07:35] 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.
[07:56] 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.
[08:17] 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.
[08:35] 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.
[09:22] 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.
[09:36] 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.
[09:51] 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.
[10:08] Na plataforma, deixaremos alguns conteúdos que vão apontar para outros ambientes que incluem os testes End-to-End nos materiais complementares.
[10:16] 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.
[10:40] 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.
[10:58] 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.
[11:24] Então vamos agora descobrir um pouco mais sobre como podemos começar a trabalhar com os testes em si no Node.js.