Você está na página 1de 30

Capítulo 3.

Teste

Vamos começar com o que está erradocom testes.

“Escrever testes demora muito. Estamos indo rápido demais para isso.”

“É um código extra para manter.”

“É para isso que serve o controle de qualidade.”

“Ele não detecta erros suficientes.”

“Não detecta os erros importantes.”

“Este é apenas um pequeno roteiro/mudança simples. Não precisa de teste.”

“Mas o código funciona.”

“O chefe/cliente está pagando por recursos, não por testes.”

“Ninguém mais na equipe se importa e vai quebrar a suíte de testes e eu estarei ten-
tando ser o comissário Gordon em uma Gotham enlouquecida.”
SOBRE ESSA ÚLTIMA CITAÇÃO...

Este é realmente muito mais difícil. Aqui, testar não é o problema. Essa dinâmica su-
gere uma equipe pequena e/ou inexperiente com falta de liderança.As mudanças
nessa perspectiva de teste e qualidade podem evoluir ao longo do tempo (lentamente,
como em uma pessoa de cada vez) ou ser impostas de cima (improvável sem uma
nova liderança).

Infelizmente, se você estiver em uma equipe cheia de “cowboys” – codificadores que


apenas enviam código, ignorando qualidade e testes – os resultados mais prováveis ​
são frustração e quebras imprevisíveis (e horas imprevisíveis).

“Deixe essa equipe tóxica imediatamente” não é a única solução, pois pode haver ou-
tros benefícios e restrições em sua situação. Mas os projetos que têm algum foco na
qualidade geralmente oferecem mais estabilidade (menos rotatividade), melhor re-
muneração e mais oportunidades de aprendizado.
De todas as possíveis citações do espantalho listadas, esta se destaca como aquela em
que a solução não é simplesmente “reconhecer e aproveitar os benefícios do teste de-
pois de se sentir confortável com ele”. Mas se a cultura de engenharia desencoraja ati-
vamente os testes, está desencorajando ativamente a qualidade.É mais difícil mudar a
cultura do que sua própria visão pessoal e experiência com testes. Se você não estiver
em um papel de liderança, seu desenvolvimento pessoal será mais bem servido evi-
tando ou deixando esses projetos.

À primeira vista, “O chefe/cliente está pagando pelos recursos, não pelos testes” tam-
bém pode parecer um problema cultural. No entanto, é improvável que isso seja real-
mente aplicado em um nível de código. Se você for eficiente em escrever testes, ape-
nas os compromissos profissionais mais curtos serão mais rápidos sem ter um con-
junto de testes instalado. Nesses casos, é melhor usar seu julgamento para escrever
software de qualidade. Nenhum chefe ou cliente razoável recusaria qualquer verifica-
ção de que o software funciona corretamente. Se você puder automatizar o processo
com eficiência, seu padrão profissional de qualidade deve incluir testes de redação. Se
você não pode fazer isso com eficiência devido à sua falta de habilidade com ferra-
mentas de teste, não pode deixar de ceder a padrões mais baixos até ganhar experiên-
cia.Primeiro, reconheça que quando você faz verificações manuais, você está tes-
tando; você simplesmente não está automatizando seus testes. Isso deve ser motiva-
ção suficiente.

Tudo isso para dizer que a solução para sua resistência interna aos testes é ficar mais
confortável com os testes à medida que você desenvolve seu padrão de qualidade. A
resistência externa, se forte o suficiente, é resistência à qualidade e ao desenvolvi-
mento profissional. Este livro deve ajudá-lo a superar a resistência interna. A resistên-
cia externa tem tudo a ver com networking e experiência na escolha de projetos.

Se você tem essas opiniões, certamente não está sozinho, e em um determinado pro-
jeto pode até estar certo. Existem dificuldades reais com testes de redação. Uma coisa
a ter em mente, porém, é que quando as coisas são frustrantes, por mais benéficas
que sejam, alguns codificadores podem sentir uma sensação de “dissonância cogni-
tiva”, o que pode aumentar a necessidade de reunir mais evidências sobre por que o
teste é inútil/ caro / não o seu trabalho. Aqui, você está agindo como a raposa de Esopo
que, depois de agarrar as uvas e falhar, se consola dizendo que elas devem ter sido
azedas de qualquer maneira. Se garantir a qualidade do software por meio de testes é
difícil, então não deve ser importante, certo?
Isso pode ser uma adaptação útil para lidar com arrependimentos e decepções na
vida que estão fora do seu controle, mas sem reconhecer os benefícios do teste, você
está fechado para uma maneira completamente diferente de escrever código. As uvas
não são azedas e, ao contrário da raposa, você pode encontrar uma escada para al-
cançá-las.

O principal objetivo do teste é ter confiança em seu código.Essa confiança não pode
nascer no vácuo. É forjado no ceticismo desenvolvido ao ver um código que erra e re-
siste à mudança. Como veremos em “Testes de Depuração e Regressão” , a confiança é
o melhor indicador do que e como testar. A maior razão para aprender a testar é de-
senvolver seus sensos de confiança e ceticismo ao olhar para uma base de código. Se
isso soa um pouco abstrato, não se preocupe. Razões mais concretas para testes serão
apresentadas na próxima seção.

Primeiro, uma nota rápida sobre alguns termos que usaremos:

Cobertura (também cobertura de código ou cobertura de teste)


Esta é uma medida, mais útil uma porcentagem, das linhas de código que são
cobertas pelos testes.  

Alto e baixo nível


Assim como o código comum, os testes podem ser amplos (alto nível) ou mais
detalhados (baixo nível).Esses são termos gerais, mas para os dois tipos mais
importantes de testes que abordaremos, o nível alto geralmente corresponde a
"testes de ponta a ponta", enquanto o nível baixo corresponde a "testes de
unidade".

Complexidade
Esta é uma medida dos caminhos através do código. Tende a ser mais casual e
geralmenteconhecido como complexidade , em vez dedo que sua complexidade
ciclomática ancestral . 

Confiança
Em última análise, é por isso que testamos.A cobertura total do teste nos dá a
confiança de que toda a base de código se comporta como pretendíamos. Exis-
tem algumas ressalvas para isso, cobertas por “Testes não funcionais” e “Desen-
volvimento orientado a testes” .

Exercido
Diz-se que uma linha de código é exercitada se for executada pelo conjunto de
testes.Se uma linha for exercida, ela terá cobertura.

Dívida técnica
Esta é a situação em que a falta de confiançae confiança (através da complexi-
dade e falta de cobertura de teste) na base de código resulta em mais suposições
e desenvolvimento mais lento em geral.

Loop de feedback
Essa é a lacuna entre escrever código e saber se está correto.Um loop de feed-
back “apertado” ou “pequeno” (em vez de um “solto” ou “longo”) é bom, porque
você sabe imediatamente quando seu código está funcionando conforme o
esperado.

Zombando e zombando
Ambas são formas de evitar o exercício direto de uma função, substituindo-a
por uma versão fictícia.A diferença entre os dois é que o mocking cria uma afir-
mação (passa/reprova parte de um teste), enquanto o stubbing não.

Os muitos porquês dos testes

1. Você já está fazendo isso!


Bem, isso é uma suposição, mas se você executarseu código no console ou abra um
arquivo HTML no navegador para verificar o comportamento, você já está tes-
tando, embora de forma lenta e propensa a erros. O teste automatizado está ape-
nas tornando esse processo repetível. Consulte “Teste manual” para obter um
exemplo.
2. A refatoração é impossível sem ele.
A refatoração, conforme discutido no Capítulo 1 , é completamente impossível sem
garantir o comportamento, o que, por sua vez, é impossível sem testes.Queremos
refatorar e melhorar a qualidade do código, certo?
3. Facilita o trabalho em equipe.
Se o seu colega de trabalho escrever algum código quebrado, o conjunto de testes
deve informar a ambos que há um problema em potencial.
4. Os testes são ideais para demonstrar (não documentar) a funcionalidade.
Fazer e manter a documentação é uma discussão totalmente diferente. Mas su-
pondo que você não tenha documentação em vigor, os testes podem demonstrar o
comportamento que você deseja do seu código. Confiar exclusivamente em testes
para documentar o código (especialmente para uma interface externa) não é uma
ótima ideia, mas é um passo à frente de ter apenas o código-fonte como referência.
5. Você não está apenas verificando o comportamento do seu código.
Nem todas as bibliotecas de software que você usa terão a mesma política de atua-
lizações e versões. Você pode atualizar involuntariamente para uma versão ruim
ou conflitante e, sem testar, não perceberia que havia quebrado seu código. Não só
isso, mas quando você traz uma nova biblioteca ou usa uma nova parte dela, você
quer confiar exclusivamente nos testes que a biblioteca tem para si mesma? E se
você modificar essa biblioteca? Você ainda pode confiar apenas nos testes de seu
desenvolvedor?
6. Os testes são cruciais para grandes atualizações.
Você realmente quer começar a usar a versão mais recente do big framework/novo
runtime. Como você pode atualizar com responsabilidade e rapidez? Apenas rapi-
damente? YOLO. Implante-o. Provavelmente está tudo bem, certo? Certo? Prova-
velmente não. Apenas com responsabilidade? Passe manualmente por todos os ca-
minhos de código possíveis e verifique se tudo faz o que deveria, construindo os
objetos de dados necessários conforme você precisa deles. Para velocidade e res-
ponsabilidade ao mesmo tempo, você precisa de um conjunto de testes. Não há ou-
tro caminho.
7. Você vai pegar bugs cedo.
Quanto mais cedo os bugs forem detectados no ciclo de desenvolvimento, mais fá-
cil será corrigi-los. Antes de escrevê-los é o ideal.Se a garantia de qualidade (QA) ou
o departamento de produto encontrar um bug, isso significa que mais pessoas de-
dicam tempo para a correção. Uma vez que atinge os clientes, você está falando so-
bre outro ciclo de produção, bem como uma potencial perda de negócios ou
confiança.
8. Os testes ajudam a suavizar os ciclos de desenvolvimento e não a “triturar”.
Testar, refatorar, melhorar a qualidade e, por fim, carregar uma pequena quanti-
dade de dívida técnica ajudará a evitar momentos em que você precisa agir rápido
e “não pode deixar de quebrar as coisas”. Isso significa longas horas, lançamentos
atrasados ​e tempo longe do que você gosta fora do trabalho.
9. 9. Seu ciclo de feedback será mais apertado.
Ao desenvolver sem testes, você está aumentando o tempo entre o desenvolvi-
mento e a verificação de que seu código está funcionando.Com os testes implemen-
tados, seu ciclo de feedback pode ser reduzido a alguns segundos. Sem eles, você
fica preso assumindo que o código funciona ou testando manualmente. Se isso leva
cinco minutos de cada vez (e fica mais longo à medida que a complexidade do pro-
grama aumenta), com que frequência você fará isso? As probabilidades são de que,
quanto mais apertado for o seu ciclo de feedback, mais frequentemente você veri-
ficará se seu código funciona e mais confiante poderá estar para fazer mais
alterações.

As muitas maneiras de testar

Nesta seção, veremos os métodos de teste.Uma coisa importante a ser observada é que
cada método de teste que estamos analisando tem três estágios: a configuração, a afir-
mação e a desmontagem.

A taxonomia dos testes varia de acordo com a organização, setor, idioma, estrutura e
ponto da história. As categorias aqui destacam os tipos mais amplos que são interes-
santes para nós na refatoração, mas esta lista não é exaustiva. Por exemplo, existem
muitas variações de testes manuais, e o que estamos chamando de testes “end-to-end”
podem ser chamados de testes de integração, testes de sistema, testes funcionais ou
qualquer outra coisa, dependendo do contexto.

Estamos principalmente interessados ​em testes que nos ajudam na refatoração – ou


seja, testes que protegem e ajudam a melhorar a qualidade do software em si, em vez
da experiência de usá-lo.

Consideramos que uma base de código tem cobertura total quando cada caminho de
código é exercido por testes de unidade, testes de ponta a ponta ou, idealmente, am-
bos. Pode parecer muito exigente insistir que todas as linhas sejam cobertas, mas ge-
ralmente quanto pior o código, pior a cobertura e vice-versa. Praticamente falando,
100% de cobertura é muito difícil de alcançar por vários motivos, especialmente
quando você está escrevendo JavaScript e contando com bibliotecas externas. Há re-
tornos decrescentes na confiança obtida por meio de mais cobertura à medida que
você se aproxima de 100% (ou mesmo “cinco noves”: 99,999%).

O teste é uma ferramenta para produzir confiança. Não é a única ferramenta. O autor
original de uma base de código ou um especialista no domínio do problema pode ob-
ter confiança de outras fontes. Testes (junto com a simplicidade do código e, quando
apropriado, comentários) são fontes especiais de confiança porque permitem que a
confiança seja transmitida junto com o código. De qualquer forma, eles não são o ob-
jetivo. Confiança é o objetivo. Os testes são uma maneira prática de criar tipos especí-
ficos de confiança no código que podem ser transferidos para outros membros da
equipe ou para você no futuro.
Para fins de refatoração, testes de ponta a ponta e testes de unidade são os mais im-
portantes dos tipos que abordaremos neste capítulo.

Testes manuais

Isso foi sugerido no início do capítulo, mas instintivamente, todos querem testar seu
código.Se você estiver trabalhando em um aplicativo da Web, isso provavelmente sig-
nifica apenas carregar a página com os objetos de dados apropriados e clicar um
pouco. Talvez você jogue uma console.log() declaração em algum lugar para ga-
rantir que as variáveis ​tenham os valores esperados. Quer você chame isso de “teste
de macaco” ou “teste manual”, “certificando-se de que funciona” ou “QAing”, essa es-
tratégia de teste é útil para exploração e depuração.

Se você se deparar com uma base de código pouco testada, é uma boa maneira de ex-
perimentar. Isso também se aplica ao nível de desenvolvimento de recursos e depura-
ção. Às vezes, você só precisa ver o que está acontecendo. Este também é um grande
componente no spiking , o processo de pesquisa que às vezes é necessário antes que
um ciclo “vermelho/verde/refator” possa ser inserido.

Dependendo de qual JavaScript você está usando (consulte o Capítulo 2 ), erros de


compilação/compilador também podem ser detectados durante esta etapa.

Teste manual documentado

Um passo para a automação do teste manual é desenvolver um plano de teste/QA. Os


melhores testes manuais são temporários ou bem documentados.Embora você queira
passar para testes de recursos e testes de unidade o mais rápido possível, às vezes a
maneira mais rápida de obter uma seção de código “coberta” é escrever um conjunto
detalhado de etapas para executar os caminhos de código relevantes. Embora não seja
automatizado, ter uma lista (semelhante ao que uma equipe de controle de qualidade
teria) pode garantir que você esteja exercitando o código de todas as maneiras neces-
sárias e torna o processo menos propenso a erros por não depender de sua memória.
Além disso, dá a outros membros de sua equipe a chance de contribuir e executar o
plano como uma “lista de verificação”. Isso é útil para tirar um pouco do peso de uma
equipe de controle de qualidade ou cumprir essa função, se ela não existir.

Se você está sem confiança no código, com uma grande implantação iminente e um
conjunto de testes disfuncional ou incompleto, esta é sua melhor opção. Documentar
seus caminhos de código em texto permite que as etapas manuais sejam repetidas e
distribuídas entre os membros da equipe.
Mesmo que a cobertura seja boa, um departamento de controle de qualidade ou de-
senvolvedores que preencham essa função podem optar por executar verificações
manualmente em um sistema específico se for especialmentevital que não quebre (às
vezes chamado de teste de fumaça ) ou contenha complexidade que seja resistente a
testes automatizados.

Testes de aprovação

Este método é um pouco complicado, mas pode funcionarpara alguns projetos e confi-
gurações de equipe.Às vezes, o resultado da execução do código seria difícil de afir-
mar automaticamente. Por exemplo, digamos que você tenha um recurso de processa-
mento de imagem em um site para cortar e redimensionar automaticamente um ava-
tar. A configuração não é problema: você simplesmente alimenta uma imagem que
tem em mãos. Mas pode ser difícil escrever uma afirmação simples que prove que a
ferramenta de corte funcionou bem. Você codifica os bytes ou pixels das imagens para
a entrada e saída desejadas?Isso tornaria os testes muito frágeis, pois quebrariam a
cada nova imagem que cortasse. Então você pula o teste completamente?

Com um teste de aprovação, você automatiza a configuração e a desmontagem do


teste, mas a afirmação (ou “aprovação”) é deixada para a entrada humana. Este con-
junto de testes mantém um registro de qual saída você aprovou. Quando o teste é exe-
cutado pela segunda vez, a mesma saída “passará” e os outros testes (que são novos
ou têm resultados diferentes da saída aprovada) são adicionados à fila de “não apro-
vados” para observação humana. Quando um membro da fila é aprovado, a nova ver-
são da saída substitui ou aumenta a memória da saída antiga (podem ser
arquivos/entradas de banco de dados na máquina de desenvolvimento ou em um ser-
vidor intermediário). Se tudo for aprovado, assume-se que o código está funcionando
corretamente.Se tudo não for aprovado, o código recebe o mesmo tratamento de qual-
quer teste com falha: ele é corrigido, esperançosamente com alguns testes de regres-
são que reproduzem as condições específicas que causaram o bug.

Esse processo pode funcionar bem quando a saída é algo como um arquivo de ima-
gem, vídeo ou áudio, onde é difícil escrever uma declaração com base na inspeção
programática.HTML pode parecer uma boa opção para esse tipo de teste, mas geral-
mente testes de ponta a ponta ou testes de unidade são mais apropriados se as afirma-
ções forem coisas como testar texto ou um elemento em uma página.Como você pode
usar um analisador HTML ou até mesmo um analisador de expressão regular (na mai-
oria das vezes) para HTML/CSS, essa saída é melhor testada por meio de testes de uni-
dade ou testes de ponta a ponta.
Assim como os testes manuais são um processo orgânico para testes individuais, de
certa forma, os testes de aprovação são uma maneira natural de testar em grupo. Em
qualquer situação em que um departamento de controle de qualidade, proprietário
do produto ou designer esteja em posição de aprovar a saída de algo, um sistema de
teste de aprovação ad hoc está em vigor.Dependendo da capacidade técnica do apro-
vador, ele pode ser responsável por lidar com a configuração programaticamente, por
meio de um teste manual documentado ou solicitando uma demonstração ao
desenvolvedor.

Os pontos fracos do sistema de teste de aprovação ad hoc são que a configuração pode
ser onerosa para o aprovador e que pode ser difícil lembrar quais saídas foram
aprovadas/rejeitadas. Isso não é um insulto à memória de ninguém envolvido. Mesmo
que estejam exclusivamente focados nesse processo, se a equipe que usa o processo
mudar, os membros que saem levam consigo todo o conhecimento das antigas
aprovações.

Mas reconheça que qualquer tentativa de estruturar esse processo produzirá algo que
parece diferente para um aprovador do que o que ele pode estar acostumado. Manter
as coisas simples para os aprovadores provavelmente significa fornecer uma lista de
URLs a serem revistos na fila.

Para o desenvolvedor, há aborrecimentos não triviais em configurar a fila, configurar


cada teste e fornecer uma maneira fácil para o aprovador revisar.

Mesmo que issoé popularmente praticado por equipes de forma ad hoc, houve pouca
inovação no espaço aparentemente provável de “estruturas de teste de aprovação”. As
questões levantadas por um sistema como este sugerem algumas razões pelas quais:

Se desenvolvimento e aprovação são dois processos distintos, onde está a lista


canônica de testes/checklists aprovados/não aprovados?
Quem é encarregado do “trabalho extra” de traduzir os requisitos do produto ou
design para essas listas?
A lista de recursos deve estar contida no código-fonte?
As falhas de teste de aprovação (após uma verificação manual) devem ser integra-
das o suficiente para falhar em uma compilação de teste?
Em caso afirmativo, como você pode evitar a desaceleração dos ciclos de teste?
Se não, qual é o mecanismo de feedback quando alguém rejeita uma especificação
de aprovação?
Haverá uma incompatibilidade de expectativas se os desenvolvedores virem os
testes de aprovação passando como a conclusão de uma tarefa?
Que interação, se houver, uma estrutura de teste de aprovação deve ter com um
sistema de rastreamento de problemas/recursos/bugs?

Pode ser difícil o suficiente para um processo ser adotado quando confinado a uma
equipe de desenvolvimento, mas no caso de uma estrutura de teste de aprovação, to-
dos os envolvidos precisam entender e concordar com as respostas às perguntas ante-
riores. Talvez algum produto de Software como Serviço seja capaz de gerenciar essas
preocupações, mas as interfaces preferidas entre as diferentes equipes são variadas e
as decisões produtivas sobre quais tipos de processos mudar com uma ferramenta
como essa provavelmente não deixarão todos felizes.

RELACIONADO: TESTE DE ACEITAÇÃO

Houve tentativas de formalizar “testes de aceitação”, que exigem rigorosamente a cri-


ação de especificações que correspondam às histórias de usuários.Se você está inte-
ressado neste tipode teste, Cucumber.js seria uma estrutura para investigar.

Embora pareça atraente e especifique muita incerteza sobre os testes de aprovação,


conseguir uma equipe ou cliente multifuncional interno pode ser mais desafiador do
que o esperado.

No pior caso (mas bastante provável), os desenvolvedores acabam escrevendo outra


camada inteira de teste (em vez do cliente ou “proprietário do produto”), mas o
cliente/proprietário do produto ainda exige o mesmo nível de flexibilidade de pro-
cesso que os testes pretendem para se proteger.

Confusamente, alguns frameworks que são descritos como frameworks de “teste de


aceitação” não insistem em sintaxe “semelhante ao inglês” e não implicam em um
processo complexo que começa com um não desenvolvedor escrevendo a especifica-
ção na referida sintaxe semelhante ao inglês. Em vez disso, eles fornecem APIs de alto
nível para eventos como clicar e fazer login, mas estes são claramente códigos e não
obscurecidos por uma camada de linguagem que é convertida em código.

Essas APIs de alto nível são úteis para testes de ponta a ponta, mas tenha cuidado com
estruturas que tentam automatizar a aceitação real de tarefas. Requisitos se transfor-
mando magicamente em código, e código preenchendo requisitos magicamente , tende a
ser a mágica que é a engenharia de software, que não é realmente mágica e provavel-
mente envolve humanos conversando entre si. Ta-da.
Testes de ponta a ponta

Finalmente, as coisas boas.Esses testes destinam-se a automatizar as interações reais


que um testador manual poderia realizar com a interface fornecida ao usuário final.
No caso do aplicativo da web, isso significa se inscrever, clicar em um botão, visuali-
zar uma página da web, baixar um arquivo e assim por diante.

Para testes como esses, o código deve ser exercitado em colaboração com outras par-
tes da base de código. Zombar e stubing devem serevitados, exceto quando absoluta-
mente necessário (geralmente envolvendo o sistema de arquivos ou requisições remo-
tas da web) para que esses testes possam abranger a integração entre os diferentes
componentes do sistema.

Esses testes são lentos e simulam a experiência dos usuários finais. Se você deseja di-
vidir as suítes de teste em duas, uma rápida e outra lenta, uma suíte de ponta a ponta
e uma suíte de teste de unidade (coberto a seguir) são o que você deseja. Estes tam-
bém são chamados de testes de alto nível e de baixo nível . Como você deve se lembrar
no início deste capítulo, “alto nível” significa ter uma visão mais ampla e fazer com
que seu código se preocupe mais com a integração de partes, enquanto “baixo nível”
significa estar mais focado nos detalhes.

Testes de unidade

Os testes unitários sãorápido e singularfocado em “unidades”. O que é uma unidade ?


Em algumas linguagens, a resposta seria “principalmente classes e suas funções”. Em
JavaScript, podemos significar arquivos, módulos, classes, funções, objetos ou
pacotes.Seja qual for o método de abstração que forma uma unidade, o foco dos testes
de unidade está no comportamento das entradas e saídas das funções para cada
unidade.

Se fingirmos que o ecossistema do JavaScript era mais simples e continha apenas clas-
ses, isso significaria testar as entradas e saídas dentro dessa classe, juntamente com a
criação de objetos da classe.
FUNÇÕES “PRIVADAS”

É um pouco simplificado dizer que as unidades JavaScript, sejam elas quais forem
(classes? escopo da função? módulos?), são divididas em métodos “privados” e “públi-
cos”.Em pacotes/módulos, métodos privados seriam as funções que não são exporta-
das. Em classes e objetos, você tem controle explícito sobre o que é “privado” em certo
sentido, mas isso necessariamente envolve configuração extra e/ou cenários de teste
complicados.

De qualquer forma, uma recomendação popular é testar apenas métodos


públicos.Isso permite que seus métodos públicos se concentrem na interface de entra-
das e saídas, pois o código para métodos privados ainda será exercitado quando você
executar os métodos públicos que os utilizam. Isso deixa você com um pouco de flexi-
bilidade para alterar os detalhes de implementação dos métodos privados, sem ter
que reescrever os testes (como mencionado anteriormente, os testes que quebram
quando uma interface não mudou podem ser chamados de brittle ).

Esta é uma boa diretriz em geral, com testes também seguindo o mantra “código para
uma interface, não uma implementação”, mas nem sempre se alinha com a priori-
dade de “confiança de código”. Se você não puder confiar em um método público sem
testar sua implementação, sinta-se à vontade para ignorar este conselho. Veremos um
exemplo de quando isso pode acontecer no próximo capítulo, quando lidarmos com
aleatoriedade.

Ao contrário dos testes de ponta a ponta, estamos preocupados apenas com o compor-
tamento independente das funções de nossas unidades. Estes são testes de baixo ní-
vel, em vez de testes de alto nível. Isso significa que nos pontos de integração entre as
unidades, devemos nos sentir à vontade para simular e stub de forma mais liberal do
que com testes de ponta a ponta.Isso nos ajuda a manter o foco nos detalhes e deixar
os testes de ponta a ponta encarregados dos pontos de integração. Além disso, se você
evitar carregar toda a sua estrutura e todos os pacotes que usar, além de falsificar as
chamadas para serviços remotos e possivelmente o sistema de arquivos e o banco de
dados também, esse conjunto de testes permanecerá rápido, mesmo à medida que
cresce.
ONDE OS FRAMEWORKS PODEM DECEPCIONÁ-LO

Os frameworks geralmente vêm com seus próprios padrões de teste.Eles podem estar
relacionados ao diretório em que o código reside, em vez de ser um teste de unidade
ou um teste de ponta a ponta. Isso é lamentável por duas razões. Primeiro, ele incen-
tiva um conjunto de testes, em vez de um conjunto rápido (executado com frequência)
e um conjunto lento (executado com alguma frequência).

A divisão por diretório de aplicativos também pode incentivar testes que são parte de
teste de unidade e parte de teste de ponta a ponta. Se você estiver testando uma ins-
crição em um aplicativo da Web, será necessário carregar o banco de dados? Para um
teste de ponta a ponta, a resposta provavelmente é sim. Para um teste de unidade, a
resposta provavelmente é não. Ter testes lentos (end-to-end) e testes rápidos (unit)
para diferenciar esse comportamento seria o ideal.

Essa divisão é erodida em parte porque coloquialmente, os testes podem ser conheci-
dos como testes de recursos, testes de modelo, testes de serviços, testes funcionais ou
qualquer outra coisa, se o diretório de teste espelhar o diretório de aplicativos. À me-
dida que um aplicativo cresce, isso pode resultar em um conjunto de testes grande e
lento (com testes rápidos misturados). Uma vez que você tenha um conjunto de testes
lento, é difícil se aprofundar e dividi-lo em testes necessariamente lentos e provavel-
mentetestes rápidos. Felizmente, a estrutura do seu aplicativo pode fornecer dicas so-
bre quais pastas devem ser rápidas e quais pastas devem ser lentas (com base em se
os arquivos dentro delas tendem a realizar operações de alto ou baixo nível). Após
essa divisão, é provável que seja necessário trabalho adicional para remover as de-
pendências de carregamento dos testes de unidade potencialmente rápidos. Como
eles são criados sem o desempenho em mente, é provável que carreguem muito do
aplicativo, do banco de dados e das dependências externas.

Uma vantagem para a organização padrão que uma estrutura pode fornecer é que os
testes podem ser mais fáceis de raciocinar e escrever para começar.Embora em algum
momento essa abordagem possa ser indesejável, ter um conjunto de testes com boa
cobertura é melhor do que dois com arquitetura um pouco melhor, mas cobertura
pior.

Tudo isso para dizer, resolva o problema imediato, mas fique atento aos problemas
futuros também. Se você tiver uma cobertura ruim, isso tem prioridade. Se o seu con-
junto de testes for tão lento que ninguém o executará, um problema que você prova-
velmente terá como resultado de muitos testes e boa cobertura, concentre seus esfor-
ços nesse problema.

Seu conjunto de testes, assim como qualquer código, deve ser escrito para servir ao
seu propósito principal antes do ajuste de desempenho. Cobertura e confiança vêm
em primeiro lugar, mas se seu pacote ficar atolado a ponto de não ser executado com
frequência, você deve abordar o desempenho. Você pode fazer isso alugando a arqui-
tetura de execução de teste online, paralelizando testes, dividindo seus testes em con-
juntos lentos e rápidos e simulando/stubbing chamadas para sistemas que são parti-
cularmente lentos.

Teste não funcional

No contexto de refatoração, o teste não funcional não contribui diretamente para


atingir nossas metas de qualidade de código.Nem contribui para a confiança de que o
código funciona. As técnicas de teste não funcionais incluem:

Teste de performance
Testando usabilidade
Teste de jogo
Teste de segurança
Teste de acessibilidade
Teste de localização

Os resultados desses tipos de testes contribuem para a criação de novos recursos e


problemas (de forma mais ampla do que inicialmente podem ser considerados bugs) a
serem corrigidos. Seu jogo é divertido? As pessoas podem usar seu programa? E os
usuários experientes? A primeira experiência é interessante? E as pessoas com defici-
ência visual? As políticas de segurança negligentes levarão a uma violação de dados
ou impedirão colaborações com empresas parceiras ou clientes?

Testes unitários e de ponta a ponta levarão a cobertura, confiança e a chance de refa-


torar, mas não abordarão essas questões diretamente.
T E N T E C A P T C H A D E Á U D I O E M A L G U M M O M E N TO

Alguns captchas de áudio em sites são terrivelmente difíceis. Mas é pior do que isso.A experiên-
cia muitas vezes começa com a imagem de alguém em uma cadeira de rodas, o que certamente
não se aplica a todas as pessoas com deficiência visual. E o áudio que se segue é muitas vezes
perturbador e assustador, além de ser difícil de analisar. Em suma, é um processo terrivel-
mente hostil e frustrante.

Em uma palestra, o especialista em acessibilidade Robert Christopherson conduziu um experi-


mento com os participantes usando um captcha de áudio. Ele tocou duas vezes e fez com que
todos escrevessem o que achavam que ouviram e depois comparassem com a pessoa ao lado
deles. Finalmente, ele perguntou quantas pessoas haviam escrito a mesma coisa que seu vizi-
nho – zero em cerca de mil.

Ignorar testes não funcionais necessariamente levará a ignorar algumas pessoas, o que está em
algum lugar entre mesquinho e ilegal. Testes não funcionais são muito importantes, mas não
são o foco deste livro.

O teste não funcional, seja para acessibilidade ou não, é fundamental para o coração
de um projeto. Como este livro é focado estritamente na qualidade técnica, não pode-
mos deixar de encobrir as muitas disciplinas envolvidas em testes não funcionais,
mas também não podemos deixar o tópico sem uma recomendação para passar mais
tempo aprendendo sobre essas ideias. Não só isso, mas também considerar que uma
equipe diversificada, funcionalmente, demograficamente e em experiências de vida,
pode ajudar a erradicar rapidamente os problemas mais flagrantes e tornar as nuan-
ces mais claras.

Dito tudo isso, essas técnicas de teste tendem a gerar tarefas


(bugs/recursos/aprimoramentos) que precisarão de atenção além do que o roteiro ób-
vio do produto indica. Isso significa mais alterações de código e potencialmente mais
complexidade. Você deseja atender a essa complexidade com confiança, assim como
faria com os recursos do produto principal. A confiança para ser flexível, corrigir
bugs e adicionar recursos vem dos testes.

Outros tipos de teste de interesse

Abordaremos isso em detalhes no próximo capítulo, mas essa classificação diz res-
peito a diferentes maneiras pelas quais seus testes podem interagir com o código de
implementação.Todos esses três tipos podem ser de alto nível ou de baixo nível:
Testes de recursos
Esses são os testes que você escreve para novos recursos.É útil escrever os tes-
tes primeiro (use test-driven development, ou TDD), como será demonstrado no
próximo capítulo.

Testes de regressão
Esses testes destinam-se inicialmente a reproduzir um bug, após o que são fei-
tas alterações no código de implementação para corrigir o bug.Isso garante co-
bertura para que esse bug não apareça novamente.

Testes de caracterização
Você os escreve para código não testado para adicionar cobertura.O processo
começa com a observação do código por meio do teste e termina com testes uni-
tários ou de ponta a ponta que funcionam como se você tivesse escrito um teste
de recurso. Se você quiser seguir o TDD, mas o código de implementação já está
escrito (mesmo que você tenha acabado de fazê-lo), você deve considerar escre-
ver esse tipo de teste ou reverter temporariamente o código de implementação
(ou comentá-lo) para escrever o teste primeiro. É assim que você pode garantir
uma boa cobertura.

Ferramentas e Processos

Espero que, neste ponto, você esteja totalmente de acordo com o “porquê” por trás
dos testes e entenda como os testes de unidade e de ponta a ponta formam o núcleo
para ter confiança em seu código.

Há um último problema que nósprecisa abordar neste capítulo: testar é difícil . Já es-
crevemos o código, e agora temos que fazer um trabalho extra para escrever o código
que o utiliza de forma estruturada e abrangente? Isso é frustrante (e também para
trás se você estiver fazendo TDD, mas chegaremos a isso em algumas páginas curtas).

O problema maior é que a qualidade é difícil . Felizmente,há mais do que apenas con-
selhos sobre como fazer isso: há processos e ferramentas, bem como uma boa pes-
quisa de meio século sobre como incentivar software de qualidade.

Nem todas essas ferramentas e processos são ótimos para todos os projetos e equipes.
Você pode até identificar um programador solitário ocasional que evita processos, fer-
ramentas e, às vezes, testes completamente.Ela pode ser profundamente criativa e
prolífica, a ponto de você vê-la e se perguntar se é assim que todo software deve ser
desenvolvido.

Em softwares complexos, mantidos por equipes de codificadores de diversos níveis de


habilidade, responsáveis ​por prazos e orçamentos reais, essa abordagemresultará em
dívida técnica e informações “isoladas” que não são bem compreendidas por toda a
equipe. Processos e ferramentas de qualidade aumentam com a complexidade de pro-
jetos e equipes, mas não existe uma situação única para todos.

Processos de Qualidade

Padrões de codificação e guias de estilo

O processo mais simples de usar para ajudar na cobertura de testes equalidade é uma
equipe adotar certos padrões.Essestipicamenteviver em um guia de estilo e incluir de-
talhescomo “Use const e let em vez de var ”, bem como diretrizes mais gerais
como “Todo novo código deve ser testado” e “Todas as correções de bugs devem ter
um teste que reproduza o bug”. Um documento podeabrangem todos os sistemas e lin-
guagens que uma equipe usa, ou eles podem ser divididos em vários documentos (um
guia de estilo CSS, um guia de estilo frontend, um guia de estilo de teste, etc.).

Desenvolver tal guia de forma colaborativa com os membros da equipe, e revisitá-lo


de tempos em tempos, pode torná-lo flexível o suficiente para adotar as ideias em mu-
dança do que “bom” significa para a equipe. Ter este guia em vigor fornece uma boa
base para os outros processos abordados aqui. Além disso, em um mundo ideal, a
maior parte deste guia de estilo também é executável, o que significa que você tem a
capacidade de verificar uma grande classe de violações do guia de estilo com muita
facilidade.

O termo “guia de estilo” também pode se referir a um documento de design que des-
creve a aparência do site. Aspectos disso podem até estar em um formulário externo
do tipo “kit de imprensa” com instruções (“Use este logotipo ao falar sobre nós”,
“Nosso nome está escrito em MAIÚSCULA” etc.) Esses são documentos distintos. Colo-
car documentos como esses no mesmo lugar que o guia de estilo de codificação não é
recomendado.

Encontro de felicidade do desenvolvedor

Outro processo leve e focado na qualidade que vale a pena considerar é uma reunião
curta e semanalas vezesconhecido como felicidade do desenvolvedor  (esse nome pode
não ser bem recebido por não codificadores na organização), qualidade de engenharia
ou reunião de dívida técnica . O objetivo da reunião é reconhecer áreas na base de có-
digo que têm problemas de qualidade que valem a pena serem abordados.Se os ge-
rentes de produto controlarem rigidamente a fila de tarefas futuras, esse processo
permitirá que um dos desenvolvedores sempre defenda a inclusão de uma determi-
nada tarefa na fila. Alternativamente, desenvolvedores ou gerentes de produto po-
dem alocar um orçamento semanal para esses tipos de tarefas.

No entanto, as tarefas recebem prioridade, esse processo permite coisas como acele-
rar o conjunto de testes; adicionar cobertura de teste a um módulo legado; ou refato-
rar tarefas a serem tratadas de uma maneira que não surpreenda ninguém, mante-
nha um foco de qualidade e permita que a pior dívida técnica seja revelada, ampla-
mente compreendida e então paga.

Programação em pares

O emparelhamento , também conhecido como programação em pares , é uma ideia


simples, mas implementá-lo em uma equipe pode ser difícil.Basicamente, dois progra-
madores lidam com um problema, lado a lado, com uma pessoa executando o teclado
(“conduzindo”) enquanto a outra observa a tela e às vezes tem um computador adicio-
nal disponível para pesquisa e verificações rápidas de sanidade sem comprometer o
desenvolvimento principal. máquina. Normalmente, a função de “motorista” é pas-
sada para frente e para trás, às vezes em intervalos regulares e predeterminados.

O emparelhamento pode levar a uma qualidade mais alta porque ajuda a detectar pe-
quenos bugs, até mesmo erros de digitação, imediatamente. Além disso, perguntas so-
bre qualidade (“Isso é muito hacky?” ou “Devemos testar isso também?”) surgem com
frequência. Discutir abertamente esses detalhes pode levar a um código mais robusto
e maior cobertura de teste. Outro benefício é que as informações sobre o sistema são
mantidas por pelo menos duas pessoas. Não apenas isso, mas ajuda outros conheci-
mentos, de algoritmos a atalhos de teclado, a se espalhar por uma organização.

Compartilhamento de conhecimento, foco, código de alta qualidade e empresa — qual


é a desvantagem? Primeiro, como em outras iniciativas baseadas em qualidade, pode
ser difícil justificar a despesa inicial. “Dois 'recursos' de programação estão traba-
lhando em uma tarefa. Que desperdício!" Em segundo lugar, esse processo exige foco
total e pode ser bastante exaustivo para os programadores.Por esta razão, o empare-
lhamento é frequentemente visto juntamente com o uso da técnica “pomodoro”, onde
o foco é obrigatório em incrementos de 25 minutos, com intervalos de 5 minutos entre
eles. Terceiro, esse processo pode parecer ofensivo e frustrante para os programado-
res (especialmente os de alto desempenho) que pensam que sua produção é retardada
por ele. Muitas vezes isso acontece quando há uma grande lacuna de experiência en-
tre os parceiros de emparelhamento. Embora essa diferença de habilidade potencial-
mente beneficie mais a equipe, permitindo a maior quantidade de transferência de
conhecimento, nem todo programador quer atuar como mentor. Se for uma priori-
dade manter os colaboradores individuais avessos aos pares felizes e na equipe, um
mandato em toda a equipe pode ser uma abordagem muito agressiva.

Experimentar o emparelhamento e obter feedback é uma boa ideia, mas as equipes


tendem a emparelhar com muita frequência ou muito raramente. Sem um mandato
ou, pelo menos, uma atitude genuinamente de apoio da equipe e da gerência, apesar
de seus benefícios, pode se desenvolver uma situação em que aqueles que formariam
um par ficam relutantes em fazê-lo por medo de serem vistos como ineficazes por
conta própria. O ceticismo em relação ao processo por membros da equipe ou geren-
tes avessos aos pares alimentará essa relutância. Então “emparelhamento” assume
um novo significado, onde as pessoas “emparelham” quando estão presas. Assim, um
ciclo se forma onde o emparelhamento é estigmatizado. Portanto, embora o empare-
lhamento não seja “tudo ou nada”, provavelmente é “principalmente ou mal”.

UMA NOTA SOBRE “RECURSOS”

Quando as pessoas (geralmente gerentes de projeto/produto) se referem amembros da


equipe no design, desenvolvimento ou qualquer outro departamento como “recur-
sos”, como em “Precisamos de mais recursos de programação neste projeto” ou “Pre-
ciso de um recurso para meu projeto”, tente dar a eles um olhar interrogativo e dizer
“ O que? Ah... você quer dizer pessoas ?

Existem dois problemas aqui. Primeiro, esperamos que sua equipe seja composta por
programadores com diversas habilidades e experiências, e pensar neles como unida-
des intercambiáveis ​não é um bom começo para garantir que um projeto seja bem
executado. Em segundo lugar, definir pessoas (especialmente em seus rostos) estrita-
mente por sua função está em algum lugar entre não profissional e mecanicamente
desumanizante.

Três variações (não mutuamente exclusivas)no emparelhamento, vale a pena notar o


emparelhamento TDD , o emparelhamento remoto e o emparelhamento promíscuo .
Com o emparelhamento TDD, o papel principal tende a alternar com a primeira pes-
soa escrevendo um teste e a segunda pessoa implementando o código para fazer o
teste passar.No emparelhamento remoto, as pessoas que não estão no mesmo local fí-
sico compartilham suas telas com canais de áudio e vídeo abertos. Em emparelha-
mento promíscuo, ao contrário do emparelhamento dedicado , os pares de pessoas são
alternados dia a dia, semana a semana, sprint a sprint ou projeto a projeto.

Revisão de código

Outra técnica que ajuda a garantir a qualidadecolocando outroo conjunto de olhos no


código é revisão de código . Por meio desse processo, quando o código é “concluído”,
ele é colocado em uma fase de revisão em que outro programador o analisa, verifi-
cando violações do guia de estilo, bugs e cobertura abrangente de teste. Isso também
pode ser combinado com um passe de QA dos desenvolvedores (especialmente em
equipes sem um departamento de QA), onde o programador de revisão executa manu-
almente o código e o compara com a descrição da tarefa (ou mais formalmente, crité-
rios de aceitação ).

Embora a programação em pares e a revisão de código não ajudem diretamente a su-


perar a aversão ao teste, elas fornecerão oportunidades para se concentrar no teste e
na qualidade. O suficiente dessas experiências deve ajudar a fornecer uma perspec-
tiva mais sutil sobre o teste do que a refletida pelas citações usadas para iniciar este
capítulo. Ambos os processos oferecem oportunidades para estabelecer normas e re-
forçar a cultura.

Desenvolvimento orientado a testes

Se você tiver o básico de testes, o desenvolvimento orientado a testes (TDD) pode pro-
duzir código de qualidade com boa cobertura de teste.A desvantagem é que é um des-
vio significativo de um processo de teste depois que o código de implementação é es-
crito. A vantagem é que o TDD, por meio do ciclo vermelho/verde/refatorar, pode for-
necer orientação durante todo o desenvolvimento. Além disso, se o TDD for seguido
estritamente, o código de implementação será escrito apenas para obter um teste de
aprovação. Isso significa que nenhum código é escrito que não tenha cobertura e, por-
tanto, nenhum código é escrito que não possa ser refatorado.

Um desafio para um codificador que usa TDD é que às vezes não é óbvio como testar,
ou mesmo escrever o código de implementação para começar.Aqui, uma fase explora-
tória conhecida como spiking é sugerida, onde o código de implementação é tentado
antes que os testes sejam escritos. Defensores rigorosos do TDD aconselharão a exclu-
são ou comentário desse código de pico assim que o codificador entender a tarefa bem
o suficiente para escrever testes e implementá-los, como o ciclo normal
vermelho/verde/refatorar.
Um termo quevocê verá com frequência ao lado do TDD o BDD (desenvolvimento ori-
entado a comportamento). Basicamente, esse processo é extremamente semelhante
ao TDD, mas é feito na perspectiva do usuário final. Isso implica em dois pontos prin-
cipais de importância. Primeiro, os testes tendem a ser de alto nível, de ponta a ponta.
Em segundo lugar, dentro do ciclo vermelho/verde/refatorar dos testes de ponta a
ponta do BDD, existem ciclos vermelho/verde/refator menores para os testes unitários
do TDD.

Vamos dar uma olhada em um diagrama ligeiramente complexo de um ciclo


vermelho/verde/refatorar ( Figura 3-1 ).

Figura 3-1. Um ciclo vermelho/verde/refatorar

Uau. Ok, isso pode parecer um pouco louco, mas existem apenas algumas complica-
ções aqui, e isso deve ajudá-lo se você estiver se perguntando em que parte do ciclo
vermelho/verde/refatorar você está.

Começando no canto superior esquerdo, escrevemos um teste com falha, que nos co-
loca em um estado vermelho. Temos três possibilidades a partir daí (um teste repro-
vado). Poderíamos seguir a seta para a direita (estado vermelho superior e médio) e
escrever outro teste com falha (então teríamos dois). Ou, se possível, poderíamos se-
guir a seta longa até o estado verde (implementar o teste). A possibilidade mais com-
plicada existe quando não podemos implementar o teste para que ele passe imediata-
mente. Nesse caso, seguimos a seta curta para baixo, onde encontramos outro ciclo
igual a este. Suponha que estamos presos lá por enquanto, mas estamos sempre livres
para criar novos testes com falha em qualquer ciclo que iniciamos.

Se algum teste (em qualquer nível) estiver no estado verde no meio, podemos fazer
uma de duas coisas: refatorar ou chamar de concluído e considerar este teste e código
completos (sair do ciclo). Quando começamos a refatoração, podemos continuar refa-
torando ou considerar este teste e código completos (sair do ciclo).

Quando todos os testes estiverem concluídos, as setas podem sair do ciclo. Se estamos
em um ciclo interno, isso significa que passamos para a fase verde do ciclo externo. Se
estivermos no ciclo externo e não pudermos pensar em mais nenhum teste para es-
crever (sem mais vermelhos para colocar na linha superior), então terminamos.

Uma sutileza nesse processo que vale a pena notar é que a criação de um novo teste
vermelho após um teste verde é recomendada somente após você ter pelo menos con-
siderado uma fase de refatoração. Se você seguir em frente imediatamente, não há in-
dicação clara de que mais trabalho deve ser feito. Por outro lado, se você acabou de
refatorar algo, está livre para seguir em frente. Da mesma forma, se você escreveu um
teste com falha (estado vermelho), se escrever outro caso de teste, sua estrutura de
teste ainda informará que há mais trabalho a ser feito no primeiro, para que você não
se perca lá.

Continuando no diagrama, aquié um exemplo concreto de um ciclo TDD contendo ou-


tro ciclo TDD:

1. Falha no teste de alto nível (vermelho) é escrito: “Um cliente pode fazer login”.
1. Falha no teste de baixo nível é escrito: “Route '/login' retorna 200 respostas.”
2. O código de roteamento é escrito para passar no teste de baixo nível. Podemos
refatorar este código escrito no ciclo interno agora.
3. O teste de alto nível ainda está falhando, então um novo teste de baixo nível
com falha é escrito: “A rota de postagem do formulário em '/login_post' deve re-
direcionar para '/' para e-mail e senha válidos.”
4. O código é escrito para lidar com a postagem bem-sucedida do e-mail/senha e
retornar a página inicial logada. Este teste de ciclo interno está passando, então
podemos refatorar este código escrito no ciclo interno agora.
2. Agora, tanto o segundo teste de baixo nível quanto o teste de alto nível estão
passando.
3. Uma vez que tudo em nosso teste de ciclo externo esteja verde, estamos livres para
refatorar o ciclo externo como acharmos melhor. E temos dois níveis de teste para
garantir que nosso código continue se comportando.
Para fins de refatoração, usar uma metodologia como BDD não importa muito. O que
importa é que você tenha uma boa cobertura em seu código, preferencialmente de
testes unitários e testes de ponta a ponta. Se eles foram criados de maneira “teste pri-
meiro” via TDD ou BDD, tudo bem. Mas o aspecto do teste da perspectiva do usuário
final por meio do BDD não é crítico. É possível criar testes de alto nível com boa co-
bertura que não testam dessa perspectiva e que não são escritos antes do código de
implementação.

PROCESSOS DE QUALIDADE COMO TEAM E PERSONAL BRANDING

Muitas vezes você verá metodologias de desenvolvimento e técnicas de aprimora-


mento de qualidade usadas como prova social.Os candidatos a emprego os adicionam
aos seus currículos, as páginas de contratação os exibem nas listas de empregos e as
principais consultorias constroem suas marcas em torno deles.

Embora no dia-a-dia possa haver um foco em “fazer as coisas” (potencialmente em de-


trimento da qualidade), estar confortável com essas técnicas e ferramentas significa
ter acesso a melhores oportunidades de emprego, sejam elas com empresas ou clien-
tes.

Ferramentas para Qualidade

Passando para as ferramentas de teste... isso pode ser um livro em si.Embora estrutu-
ras e ferramentas individuais aumentem e diminuam em popularidade, saber quais
tipos de ferramentas estão disponíveis continuará sendo útil. Às vezes, as estrelas do
GitHub ou npmjs são um bom proxy para qualidade e popularidade. Pesquisar por “js
<tooltype>” (por exemplo, “js test covers”) com seu mecanismo de pesquisa preferido
geralmente retorna resultados decentes. É melhor aprender bem uma ou duas ver-
sões de cada uma dessas ferramentas, mas com o desenvolvimento de pacotes de có-
digo aberto do JavaScript sendo tão expansivo, esteja preparado para se adaptar fre-
quentemente a ferramentas novas, mas semelhantes.

Controle de versão

Antes que qualquer outra ferramenta seja mencionada (caso você a tenha perdido no
Capítulo 1 ), é fundamental que seu projeto esteja sob controle de versão, de preferên-
cia com backups em algum lugar que não seja apenas o seu computador.Se a falta de
testes deve produzir ceticismo em vez de confiança, a falta de controle de versão deve
produzir desconforto, se não terror. O controle de versão, com um backup online (no
momento da redação deste artigo, Git + GitHub é recomendado), garante que seu có-
digo não desapareça completamente. Nenhuma quantidade de qualidade no código
importa se ele pode ser eliminado. Além disso, muitas das ferramentas abordadas
aqui contam com software com versão para integração e para demonstrar o
progresso.

Estruturas de teste

Eles variam significativamente com base no JavaScript que você está usando (consulte
o Capítulo 2 ) e em quantas ferramentas você deseja agrupar.Em geral, eles permitem
que você especifique casos de teste em um arquivo (ou muitos) e tenha uma interface
de linha de comando que você pode usar para executar o conjunto de testes. Uma es-
trutura de teste também pode incluir muitas das ferramentas listadas a seguir. Alguns
frameworks são específicos para o código front-end ou back-end. Alguns são específi-
cos para a estrutura JavaScript. Alguns são específicos para testes de alto nível, baixo
nível ou de aceitação. Eles geralmente ditam como seus arquivos de teste são grava-
dos, além de fornecer um executor de testes. O executor de teste executará o conjunto
(geralmente permitindo o direcionamento de um diretório, arquivo ou caso de teste
individual específico) e gerará erros e falhas da execução. Ele provavelmente também
será responsável pelas fases de configuração e desmontagem de sua execução de
teste. Além de carregar o código, isso também pode significar colocar o banco de da-
dos em um estado específico (“semeá-lo”) antes da execução do teste,

No próximo capítulo, usaremos alguns dos recursos mais básicos do framework de


teste Mocha. No Capítulo 9 , também testaremos uma estrutura mais leve chamada
Tape.

Bibliotecas de sintaxe de asserção/expectativa

Eles geralmente são feitos para funcionar com estruturas de teste específicas e geral-
mente vêm junto com elas.Eles são o que permitem afirmar que uma determinada
função retorna “hello world” ou que a execução de uma função com um determinado
conjunto de entradas resulta em um erro.

As bibliotecas de asserção podem ser desnecessariamente complicadas. O Capítulo 4


apresenta uma biblioteca simples de testes de asserção e caracterização chamada
desejo.

Bibliotecas específicas de domínio


Exemplos destes incluemadaptadores de banco de dados e drivers da web (para simu-
lar cliques e outras interações do site).Eles podem ser agrupados em uma estrutura
JavaScript ou estrutura de teste. Muitas vezes, eles vêm com sua própria sintaxe de
afirmação/expectativa que estende a estrutura de teste.

Fábricas e instalações

Essas ferramentas são responsáveis ​por criar objetos de banco de dados sob demanda
(fábricas) ou a partir de um arquivo especificando dados (dispositivos elétricos).Essas
bibliotecas às vezes são associadas à palavra fake ou faker . Você achará isso útil se
seus testes exigirem muito código para configurar.

Bibliotecas de mock/stubbing

Estes são às vezesassociadocom os termos mocks/mocking , stubs/stubbing , doubles e


spies . Tanto os mocks quanto os stubs permitem que você evite chamar uma determi-
nada função em seus testes, enquanto os mocks também definem uma expectativa
(um teste) de que a função é chamada. A funcionalidade geral de mocking/stubbing
geralmente é incluída como parte de uma estrutura de teste, mas também há bibliote-
cas de mocking/stubbing mais específicas que não são. Eles podem eliminar classes in-
teiras de chamadas de função, incluindo chamadas para o sistema de arquivos, banco
de dados ou quaisquer solicitações externas da web.

Ferramentas de compilação/tarefa/empacotamento

O ecossistema JavaScript tem uma tonelada de ferramentas para reunir código, trans-
formá-lo e executar scripts (personalizados ou definidos por biblioteca).Eles podem
ser ditados por uma estrutura JavaScript e você pode ter alguns que se sobrepõem em
seu projeto.

Carregadores e observadores

Se você estiver executando o conjunto de testes com frequência, o que é essencial


para ter um ciclo de feedback apertado, carregar todo o aplicativo/programa antes de
cada execução pode atrasá-lo significativamente.Um carregador pode acelerar o pro-
cesso mantendo seu aplicativo na memória.Isso geralmente é combinado com um
programa de observação que executa seu conjunto de testes quando você salva um ar-
quivo. O ciclo de feedback pode ser ainda mais apertado quando o script do observa-
dor executa de forma inteligente apenas os testes relevantes para o arquivo salvo.
Paralelizadores de execução de teste

Às vezes, incorporadas a carregadores/executores de tarefas ou estruturas de teste, es-


sas ferramentas usam vários núcleos em sua máquina, paralelizando a execução de
teste e acelerando a execução do conjunto de testes.Um cuidado aqui que vale a pena
observar é que, se seu aplicativo for muito dependente de efeitos colaterais (incluindo
o uso de um banco de dados), você poderá ver mais falhas quando os testes produzi-
rem um estado conflitante com outros testes.

Serviços de integração contínua (CI)

Esses são serviços online que executam seu conjunto de testes sob demanda ou em
eventos como confirmação no repositório de controle de versão compartilhado, às ve-
zes restrito a ramificações específicas.Eles geralmente fazem uso de paralelização (e
oferecem desempenho geral) além do que você poderia realizar em sua máquina
pessoal.

Repórteres de cobertura

Para saber se o código é seguro para refatorar, é essencial saber se o código está sufi-
cientemente coberto por testes.Se você é ou não capaz de determinar a cobertura sem
realmente executar o conjunto de testes (e, consequentemente, exercitar o código) é
uma questão acadêmica interessante de análise dinâmica versus estática, mas, feliz-
mente, as ferramentas de cobertura que determinam a cobertura executando o con-
junto de testes são abundantes. Eles podem ser executados localmente, mas geral-
mente são combinados com sistemas CI.
TESTE DE MUTAÇÃO

Se você estiver interessado em outras possibilidades usando análise dinâmica, que


ferramentas de cobertura utilizam,você pode querer verificar o teste de mutação .

Este tópico pode ser um pouco profundo, mas basicamente uma ferramenta executa
seu conjunto de testes com uma base de código mutante (mais facilmente alterando as
entradas de teste, por exemplo, uma entrada de string onde um booleano era espe-
rado) e falha quando seus testes passam , apesar da mutação código.

Isso pode permitir que você encontre casos em que os testes não exigem que o código
seja como está. Isso pode significar que o código não é exercitado, mas também pode
significar que a entrada normal não tem sentido no contexto do teste. Por exemplo, se
um parâmetro para uma função é usado de uma maneira tão geral que várias entra-
das modificadas funcionariam tão bem, isso sugere que, mesmo que a cobertura mos-
tre uma linha de código como sendo executada, o código não é suficientemente exer-
citado. Na melhor das hipóteses, você pode estar contando apenas com a coerção de
tipo no caso do JavaScript.

Dois avisos se aplicam aqui. Primeiro, o teste de mutação depende de várias variações
em seu conjunto de testes normal, portanto, tenderá a ser lento. Em segundo lugar,
pode quebrar seu código de maneiras inesperadas e complicar a fase de desmonta-
gem de seu teste – por exemplo, fazendo qualquer coisa, desde deixar seu banco de
dados de teste em um estado inesperado até realmente alterar o código em seus arqui-
vos. Verifique seu código no controle de versão e faça backup de seu banco de dados
antes de executar algo tão agressivo e imprevisível.

Damas de estilo, também conhecidos como linters

A maioria das ferramentas de qualidade não requer análise dinâmica.Eles podem ins-
pecionar arquivos sem executar o código para procurar muitos tipos de erros ou vio-
lações estilísticas. Essas verificações às vezes são executadas localmente como scripts
discretos ou por meio de serviços online depois que o código é confirmado em um re-
positório compartilhado. Para um ciclo de feedback mais apertado, essas verificações
geralmente podem ser integradas no IDE ou editor de um programador. Você não
pode fazer nada melhor do que receber notificações de erros imediatamente após es-
crevê-los. Às vezes, eles são chamados de linters . Na melhor das hipóteses, esses veri-
ficadores de estilo que você possui servem como um guia de estilo executável.
Depuradores/registradores Apoiar Sair

Às vezes, durante um pico


© 2022 (escrever
O'REILLY código
MEDIA, INC.  TERMOSexploratório
DE SERVIÇO e temporário
POLÍTICA DE PRIVACIDADE sem teste), um

caso de teste complicado ou um teste manual, pode não ficar claro quais valores as
variáveis ​têm, quais funções estão produzindo ou mesmo se uma determinada seção
do código está sendo executada. Nesses casos, depuradores e registradores são seus
amigos.No ponto do arquivo onde está a confusão, seja em seu teste ou em seu código
de implementação, você pode adicionar uma instrução de log ou definir um ponto de
interrupção de depuração . O registro às vezes lhe dará a resposta que você precisa
(por exemplo, “esta função foi alcançada” ou “este arquivo foi carregado”), mas os de-
puradores oferecem a chance de interromper a execução do código e inspecionar
qualquer variável ou chamada de função que você desejar.

Servidores de teste/QA

Pode ser difícil simular a produção no nível necessário em sua máquina de desenvol-
vimento local.Por esse motivo, servidores (ou instâncias/máquinas virtuais) que são o
mais semelhantes possíveis à produção costumam ser usados ​com um banco de dados
que possui uma versão limpa, mas representativa dos dados de produção.

O B S E RVA Ç Ã O

Se esses processos e ferramentas parecerem esmagadores, não se preocupe. Quando você pre-
cisa deles, eles podem ser úteis e, se não precisar, geralmente pode evitá-los. É bom experimen-
tar novas ferramentas como essas em projetos de hobby, mas se você exagerar, acabará gas-
tando mais tempo configurando tudo para funcionar em conjunto do que realmente fazendo o
projeto!

Empacotando

Portanto, agora temos uma boa visão geral do que é teste, bem como por que ele é útil
e completamente essencial para refatoração. No próximo capítulo, abordaremos como
testar sua base de código, independentemente do estado em que ela esteja.

Você também pode gostar