Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas


TDD - Teste Drive Development, Manuais, Projetos, Pesquisas de Metodologia

Livro sobre TDD - Testes testes tes

Tipologia: Manuais, Projetos, Pesquisas

2019

Compartilhado em 03/12/2019

dagoberto-medeiros-8
dagoberto-medeiros-8 🇧🇷

2 documentos

1 / 166

Toggle sidebar

Esta página não é visível na pré-visualização

Não perca as partes importantes!

bg1
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28
pf29
pf2a
pf2b
pf2c
pf2d
pf2e
pf2f
pf30
pf31
pf32
pf33
pf34
pf35
pf36
pf37
pf38
pf39
pf3a
pf3b
pf3c
pf3d
pf3e
pf3f
pf40
pf41
pf42
pf43
pf44
pf45
pf46
pf47
pf48
pf49
pf4a
pf4b
pf4c
pf4d
pf4e
pf4f
pf50
pf51
pf52
pf53
pf54
pf55
pf56
pf57
pf58
pf59
pf5a
pf5b
pf5c
pf5d
pf5e
pf5f
pf60
pf61
pf62
pf63
pf64

Pré-visualização parcial do texto

Baixe TDD - Teste Drive Development e outras Manuais, Projetos, Pesquisas em PDF para Metodologia, somente na Docsity!

Casa do Código

Agradecimentos

Essa talvez seja a seção mais difícil de se escrever, pois a quantidade de pessoas que participaram direta ou indiretamente do livro é muito grande. Vou começar agradecimento meu pai, mãe e irmão, que a todo momento me apoiaram na decisão de fazer um mestrado, entender como ciência deve ser feita, e que sofreram junto comigo nos momentos de grande stress (que todo mestrado proporciona!). Agradeço também ao meu orientador de mestrado e doutorado, prof. Dr. Marco Aurelio Gerosa, que me ensinou como as coisas funcionam “do lado de lá”. Sem ele, acho que esse livro seria muito diferente; seria mais apaixonado, porém menos verdadeiro. Se meu texto olha TDD de maneira fria e imparcial, a culpa é dele. Os srs. Paulo Silveira e Adriano Almeida também merecem uma lembrança. Mesmo na época em que a Casa do Código não existia de fato, eles já haviam aceitado a ideia do livro de TDD. Obrigado pela confiança. Todas as pessoas das últimas empresas em que atuei também me ajudaram muito com as incontáveis conversas de corredor sobre o assunto. Isso com certeza enrique- ceu muito o texto.

Agradeço também aos amigos José Donizetti, Guilherme Moreira e Rafael Fer- reira, que gastaram tempo lendo o livro e me dando sugestões de como melhorar.

Por fim, obrigado a você que está lendo esse livro. Espero que ele ajude.

i

Casa do Código

Quem sou eu?

Meu nome é Mauricio Aniche, e trabalho com desenvolvimento de software há por volta de 10 anos. Em boa parte desses 10 anos, atuei como consultor para diferentes empresas do mercado brasileiro e internacional. Com certeza, as linguagens mais utilizadas por mim ao longo da minha carreira foram Java, C# e C. Como sempre pulei de projeto em projeto (e, por consequência, de tecnologia em tecnologia), nunca fui a fundo em nenhum delas. Pelo contrário, sempre foquei em entender princípios que pudessem ser levados de uma para outra, para que no fim, o código saísse com qualidade, independente da tecnologia. Em meu último ano da graduação, 2007, comecei a ler mais sobre a ideia de testes automatizados e TDD. Achei muito interessante e útil a ideia de se escrever um programa para testar seu programa, e decidi praticar TDD, por conta própria, para entender melhor como ela funcionava. Gostei muito do que vi. De 2007 em diante, resolvi praticar, pesquisar e divulgar melhor minhas ideias sobre o assunto. Comecei devagar, apenas blogando o que estava na minha cabeça e que gostaria de feedback de outros desenvolvedores. Mas para fazer isso de maneira mais decente, resolvi ingressar no programa de Mestrado da Universidade de São Paulo. Lá, pesquisei sobre os efeitos da prática de TDD no design de classes. Ao longo desse tempo participei da grande maioria dos eventos relacionados ao assunto. Palestrei nos principais eventos de métodos ágeis do país (como Agile Brazil, Encontro Ágil), de desenvolvimento de software (QCON SP e DNAD), entre outros menores. Cheguei a participar de eventos internacionais também; fui o único palestrante brasileiro no Primeiro Workshop Internacional sobre TDD, em 2010, na cidade de Paris. Isso mostra também que tenho participado dos eventos acadêmicos. Em 2011, apresentei um estudo sobre TDD no WBMA (Workshop Brasileiro de Mé- todos Ágeis), e em 2012, no maior simpósio brasileiro sobre engenharia de software, o SBES.

iii

Casa do Código

Atualmente trabalho pela Caelum, como consultor e instrutor. Também sou aluno de doutorado pela Universidade de São Paulo, onde continuo a pesquisar sobre a relação dos testes de unidade e qualidade do código.

Portanto, esse é meu relacionamento com TDD. Nos últimos anos tenho olhado ele de todos os pontos de vista possíveis: de praticante, de acadêmico, de pesquisador, de apaixonado, de frio. Esse livro é o relato de tudo que aprendi nesses últimos anos.

iv

Casa do Código

que produzem. Neste livro, utilizamos Java para demonstrar os conceitos discuti- dos, mas você pode facilmente levar as discussões feitas aqui para a sua linguagem de programação favorita. Mesmo que você já pratique TDD, tenho certeza que aqui encontrará discussões interessantes sobre como a prática dá feedback sobre proble- mas de acoplamento e coesão, bem como técnicas para escrever testes melhores e mais fáceis de serem mantidos.

Testadores também podem se beneficiar deste livro, entendendo como escrever códigos de teste de qualidade, quando ou não usar TDD, e como reportar problemas de código para os desenvolvedores.

Como devo estudar?

Ao longo do livro, trabalhamos em diversos exemplos, muito similares ao mundo real. Todo capítulo possui sua parte prática e parte teórica. Na parte prática, muito código de teste é escrito. Na parte teórica, refletimos sobre o código que produzimos até aquele momento, o que foi feito de bom, o que foi feito de ruim, e melhoramos de acordo. O leitor pode refazer todos os códigos produzidos nos capítulos. Praticar TDD é essencial para que as ideias fiquem naturais. Além disso, a Caelum também dispo- nibiliza um curso online sobre testes automatizados [9], que pode ser usado como complemento desse livro. Boa leitura!

vi

Casa do Código Sumário

  • 1 Introdução Sumário
    • 1.1 Era uma vez um projeto sem testes...
    • 1.2 Por que devemos testar?
    • 1.3 Por que não testamos?
    • 1.4 Testes automatizados e TDD
    • 1.5 Conclusão
  • 2 Testes de Unidade
    • 2.1 O que é um teste de unidade?
    • 2.2 Preciso mesmo escrevê-los?
    • 2.3 O Primeiro Teste de Unidade
    • 2.4 Continuando a testar
    • 2.5 Conclusão
  • 3 Introdução ao Test-Driven Development
    • 3.1 O problema dos números romanos
    • 3.2 O primeiro teste
    • 3.3 Refletindo sobre o assunto
    • 3.4 Quais as vantagens?
    • 3.5 Um pouco da história de TDD
    • 3.6 Conclusão
  • 4 Simplicidade e Baby Steps
    • 4.1 O Problema do Cálculo de Salário
    • 4.2 Implementando da maneira mais simples possível
    • 4.3 Passos de Bebê (ou Baby Steps) Sumário Casa do Código
    • 4.4 Usando baby steps de maneira consciente
    • 4.5 Conclusão
  • 5 TDD e Design de Classes
    • 5.1 O Problema do Carrinho de Compras
    • 5.2 Testes que influenciam no design de classes
    • 5.3 Diferenças entre TDD e testes da maneira tradicional
    • 5.4 Testes como rascunho
    • 5.5 Conclusão
  • 6 Qualidade no Código do Teste
    • 6.1 Repetição de código entre testes
    • 6.2 Nomenclatura dos testes
    • 6.3 Test Data Builders
    • 6.4 Testes Repetidos
    • 6.5 Escrevendo boas asserções
    • 6.6 Testando listas
    • 6.7 Separando as Classes de Teste
    • 6.8 Conclusão
  • 7 TDD e a Coesão
    • 7.1 Novamente o Problema do Cálculo de Salário
    • 7.2 Ouvindo o feedback dos testes
    • 7.3 Testes em métodos privados?
    • 7.4 Resolvendo o Problema da Calculadora de Salário
    • 7.5 O que olhar no teste em relação a coesão?
    • 7.6 Conclusão
  • 8 TDD e o Acoplamento
    • 8.1 O Problema da Nota Fiscal
    • 8.2 Mock Objects
    • 8.3 Dependências explícitas
    • 8.4 Ouvindo o feedback dos testes
    • 8.5 Classes estáveis
    • 8.6 Resolvendo o Problema da Nota Fiscal Casa do Código Sumário
    • 8.7 Testando métodos estáticos
    • 8.8 TDD e a constante criação de interfaces
    • 8.9 O que olhar no teste em relação ao acoplamento?
    • 8.10 Conclusão
  • 9 TDD e o Encapsulamento
    • 9.1 O Problema do Processador de Boleto
    • 9.2 Ouvindo o feedback dos testes
    • 9.3 Tell, Don’t Ask e Lei de Demeter
    • 9.4 Resolvendo o Problema do Processador de Boletos
    • 9.5 O que olhar no teste em relação ao encapsulamento?
    • 9.6 Conclusão
  • 10 Testes de Integração e TDD
    • 10.1 Testes de unidade, integração e sistema
    • 10.2 Quando não usar mocks?
    • 10.3 Testes em DAOs
    • 10.4 Devo usar TDD em testes de integração?
    • 10.5 Testes em aplicações Web
    • 10.6 Conclusão
  • 11 Quando não usar TDD?
    • 11.1 Quando não praticar TDD?
    • 11.2 100% de cobertura de código?
    • 11.3 Devo testar códigos simples?
    • 11.4 Erros comuns durante a prática de TDD
    • 11.5 Como convencer seu chefe sobre TDD?
    • 11.6 TDD em Sistemas Legados
    • 11.7 Conclusão
  • 12 E agora?
    • 12.1 O que ler agora?
    • 12.2 Dificuldade no aprendizado
    • 12.3 Como interagir com outros praticantes?
    • 12.4 Conclusão Final

Sumário Casa do Código

13 Apêndice: Princípios SOLID 145 13.1 Sintomas de Projetos de Classes em Degradação............ 145 13.2 Princípios de Projeto de Classes...................... 148 13.3 Conclusão................................... 151

Índice Remissivo 152

Bibliografia 155

x

1.2. Por que devemos testar? Casa do Código

gasolinas, abastecimentos simultâneos, e etc. Mas, mesmo assim, nos revezávamos e testávamos da forma que conseguíamos. Após alguns meses de desenvolvimento, encontramos o primeiro posto disposto a participar do piloto da aplicação. Esse posto ficava em Santo Domingo, capital da República Dominicana. Eu, na época líder técnico dessa equipe, viajei para o lugar com o objetivo de acompanhar nosso produto rodando pela primeira vez. Fizemos a instalação do pro- duto pela manhã e acompanhamos nosso “bebê” rodando por todo o dia. Funcionou perfeitamente. Saímos de lá e fomos jantar em um dos melhores lugares da cidade para comemorar. Missão cumprida.

1.2 Por que devemos testar?

Voltando do jantar, fui direto pra cama, afinal no dia seguinte entenderíamos quais seriam as próximas etapas do produto. Mas às 7h da manhã, o telefone tocou. Era o responsável pelo posto de gasolina piloto. Ele me ligou justamente para contar que o posto de gasolina estava completamente parado desde as 0h: o software parou de funcionar e bloqueou completamente o posto de gasolina.

Nosso software nunca havia sido testado com uma quantidade grande de abaste- cimentos. Os postos em Santo Domingo fazem muitos pequenos abastecimentos ao longo do dia (diferente daqui, onde enchemos o tanque de uma vez). O sistema não entendeu isso muito bem, e optou por bloquear as bombas de gasolina, para evitar fraude, já que não conseguia registrar as futuras compras. O software fez com que o estabelecimento ficasse 12h parado, sem vender nada. Quanto será que isso custou ao dono do estabelecimento? Como será que foi a reação dele ao descobrir que o novo produto, no primeiro dia, causou tamanho estrago?

Os Estados Unidos estimam que bugs de software lhes custam aproximadamente 60 bilhões de dólares por ano [34]. O dinheiro que poderia estar sendo usado para erradicar a fome do planeta está sendo gasto em correções de software que não fun- cionam.

É incrível a quantidade de software que não funciona. Pergunte ao seu amigo não-técnico se ele já ficou irritado porque algum programa do seu dia a dia simples- mente parou de funcionar. Alguns bugs de software são inclusive famosos por todos: o foguete Ariane 5 explodiu por um erro de software; um hospital panamenho matou pacientes pois seu software para dosagem de remédios errou.

2

Casa do Código Capítulo 1. Introdução

1.3 Por que não testamos?

Não há um desenvolvedor que não saiba que a solução para o problema é testar seus códigos. A pergunta é: por que não testamos?

Não testamos, porque testar sai caro. Imagine o sistema em que você trabalha hoje. Se uma pessoa precisasse testá-lo do começo ao fim, quanto tempo ela levaria? Semanas? Meses? Pagar um mês de uma pessoa a cada mudança feita no código (sim, os desenvolvedores também sabem que uma mudança em um trecho pode gerar problemas em outro) é simplesmente impossível.

Testar sai caro, no fim, porque estamos pagando “a pessoa” errada para fazer o trabalho. Acho muito interessante a quantidade de tempo que gastamos criando soluções tecnológicas para resolver problemas “dos outros”. Por que não escrevemos programas que resolvam também os nossos problemas?

1.4 Testes automatizados e TDD

Uma maneira para conseguir testar o sistema todo de maneira constante e contínua a um preço justo é automatizando os testes. Ou seja, escrevendo um programa que testa o seu programa. Esse programa invocaria os comportamentos do seu sistema e garantiria que a saída é sempre a esperada. Se isso fosse realmente viável, teríamos diversas vantagens. O teste executaria muito rápido (afinal, é uma máquina!). Se ele executa rápido, logo o rodaríamos constantemente. Se os rodarmos o tempo todo, descobriríamos os problemas mais cedo, diminuindo o custo que o bug geraria. Um ponto que é sempre levantando em qualquer discussão sobre testes manuais versus testes automatizados é produtividade. O argumento mais comum é o de que agora a equipe de desenvolvimento gastará tempo escrevendo código de teste; antes ela só gastava tempo escrevendo código de produção. Portanto, essa equipe será menos produtiva. A resposta para essa pergunta é: o que produtividade? Se produtividade for me- dida através do número de linhas de código de produção escritos por dia, talvez o desenvolvedor seja sim menos produtivo. Agora, se produtividade for a quantidade de linhas de código de produção sem defeitos escritos por dia, provavelmente o de- senvolvedor será mais produtivo ao usar testes automatizados. Além disso, se analisarmos o dia a dia de um desenvolvedor que faz testes manu- ais, podemos perceber a quantidade de tempo que ele gasta com teste. Geralmente o

Capítulo 2

Testes de Unidade

2.1 O que é um teste de unidade?

Imagine-se passeando em uma loja virtual qualquer na web. Ao selecionar um pro- duto, o sistema coloca-o no seu carrinho de compras. Ao finalizar a compra, o sis- tema fala com a operadora de cartão de crédito, retira o produto do estoque, dispara um evento para que a equipe de logística separe os produtos comprados e te envia um e-mail confirmando a compra. O software que toma conta de tudo isso é complexo. Ele contém regras de negó- cio relacionadas ao carrinho de compras, ao pagamento, ao fechamento da compra. Mas, muito provavelmente, todo esse código não está implementado em apenas um único arquivo; esse sistema é composto por diversas pequenas classes, cada uma com sua tarefa específica. Desenvolvedores, quando pensam em teste de software, geralmente imaginam um teste que cobre o sistema como um todo. Um teste de unidade não se preocupa com todo o sistema; ele está interessado apenas em saber se uma pequena parte do sistema funciona.

2.2. Preciso mesmo escrevê-los? Casa do Código

Um teste de unidade testa uma única unidade do nosso sistema. Geralmente, em sistemas orientados a objetos, essa unidade é a classe. Em nosso sistema de exemplo, muito provavelmente existem classes como “CarrinhoDeCompras”, “Pedido”, e assim por diante. A ideia é termos baterias de testes de unidade separadas para cada uma dessas classes; cada bateria preocupada apenas com a sua classe.

2.2 Preciso mesmo escrevê-los?

Essa mesma loja virtual precisa encontrar, dentro do seu carrinho de compras, os produtos de maior e menor valor. Um possível algoritmo para esse problema seria percorrer a lista de produtos no carrinho, comparar um a um, e guardar sempre a referência para o menor e o maior produto encontrado até então. Em código, uma possível implementação seria:

public class MaiorEMenor {

private Produto menor; private Produto maior;

public void encontra(CarrinhoDeCompras carrinho) { for(Produto produto : carrinho.getProdutos()) { if(menor == null || produto.getValor() < menor.getValor()) { menor = produto; } else if (maior == null || produto.getValor() > maior.getValor()) { maior = produto; } } }

public Produto getMenor() { return menor; }

public Produto getMaior() { return maior; }

}

6

2.3. O Primeiro Teste de Unidade Casa do Código

O carrinho contém três produtos: liquidificador, geladeira e jogo de pratos. É fácil perceber que o jogo de pratos é o produto mais barato (R$ 70,00), enquanto que a geladeira é o mais caro (R$ 450,00). A saída do programa é exatamente igual à esperada:

O menor produto: Jogo de pratos O maior produto: Geladeira

Apesar de aparentemente funcionar, se esse código for para produção, a loja vir- tual terá problemas.

2.3 O Primeiro Teste de Unidade

A classe MaiorEMenor respondeu corretamente ao teste feito acima, mas ainda não é possível dizer se ela realmente funciona para outros cenários. Observe o código abaixo:

public class TestaMaiorEMenor { public static void main(String[] args) { CarrinhoDeCompras carrinho = new CarrinhoDeCompras(); carrinho.adiciona(new Produto("Geladeira", 450.0)); carrinho.adiciona(new Produto("Liquidificador", 250.0)); carrinho.adiciona(new Produto("Jogo de pratos", 70.0));

MaiorEMenor algoritmo = new MaiorEMenor(); algoritmo.encontra(carrinho);

System.out.println("O menor produto: " + algoritmo.getMenor().getNome()); System.out.println("O maior produto: " + algoritmo.getMaior().getNome()); } }

O código acima não é tão diferente do anterior. Os produtos, bem como os va- lores, são os mesmos; apenas a ordem em que eles são inseridos no carrinho foi tro- cada. Espera-se então que o programa produza a mesma saída. Mas, ao executá-lo, a seguinte saída é gerada:

O menor produto: Jogo de pratos Exception in thread "main" java.lang.NullPointerException at TestaMaiorEMenor.main(TestaMaiorEMenor.java:12)

8

Casa do Código Capítulo 2. Testes de Unidade

Problema! Essa não era a saída esperada! Agora está claro que a classe MaiorEMenor não funciona bem para todos os cenários. Se os produtos, por algum motivo, forem adicionados no carrinho em ordem decrescente, a classe não consegue calcular corretamente.

Uma pergunta importante para o momento é: será que o desenvolvedor, ao es- crever a classe, perceberia esse bug? Será que ele faria todos os testes necessários para garantir que a classe realmente funciona? Como discutido no capítulo anterior, equipes de software tendem a não testar software, pois o teste leva tempo e, por consequência, dinheiro. Na prática, equi- pes acabam por executar poucos testes ralos, que garantem apenas o cenário feliz e mais comum. Todos os problemas da falta de testes, que já foram discutidos ante- riormente, agora fazem sentido. Imagine se a loja virtual colocasse esse código em produção. Quantas compras seriam perdidas por causa desse problema?

Para diminuir a quantidade de bugs levados para o ambiente de produção, é ne- cessário testar o código constantemente. Idealmente, a cada alteração feita, todo o sistema deve ser testado por inteiro novamente. Mas isso deve ser feito de maneira sustentável: é impraticável pedir para que seres humanos testem o sistema inteiro a cada alteração feita por um desenvolvedor.

A solução para o problema é fazer com que a máquina teste o software. A má- quina executará o teste rapidamente e sem custo, e o desenvolvedor não gastaria mais tempo executando testes manuais, mas sim apenas em evoluir o sistema.

Escrever um teste automatizado não é tarefa tão árdua. Ele, na verdade, se parece muito com um teste manual. Imagine um desenvolvedor que deva testar o compor- tamento do carrinho de compras da loja virtual quando existem dois produtos lá ca- dastrados: primeiramente, ele “clicaria em comprar em dois produtos”, em seguida “iria para o carrinho de compras”, e por fim, verificaria “a quantidade de itens no carrinho (deve ser 2)” e o “o valor total do carrinho (que deve ser a soma dos dois produtos adicionados anteriormente)”. Ou seja, de forma generalizada, o desenvolvedor primeiro pensa em um cenário (dois produtos comprados), depois executa uma ação (vai ao carrinho de compras), e por fim, valida a saída (vê a quantidade de itens e o valor total do carrinho). A figura abaixo mostra os passos que um testador geralmente faz quando deseja testar uma funcionalidade.