Julio Biason
5 years ago
2 changed files with 140 additions and 0 deletions
@ -0,0 +1,7 @@ |
|||||||
|
+++ |
||||||
|
title = "Reveries About Testing" |
||||||
|
date = 2020-01-13 |
||||||
|
|
||||||
|
[taxonomies] |
||||||
|
tags = ["tests", "testing", "integration tests", "unit tests"] |
||||||
|
+++ |
@ -0,0 +1,133 @@ |
|||||||
|
+++ |
||||||
|
title = "Devaneios Sobre Testes" |
||||||
|
date = 2020-01-13 |
||||||
|
|
||||||
|
[taxonomies] |
||||||
|
tags = ["testes", "testes de integração", "testes unitários", "companion post"] |
||||||
|
+++ |
||||||
|
|
||||||
|
Hoje em dia, boa parte dos desenvolvedores utiliza alguma metodologia de |
||||||
|
testes. Mas o que são os testes? Para que servem? Qual o objetivo de se |
||||||
|
testar? Estamos testando as coisas certas? |
||||||
|
|
||||||
|
<!-- more --> |
||||||
|
|
||||||
|
{% note() %} |
||||||
|
Esse post acompanha a minha apresentação de [Filosofando Sobre |
||||||
|
Testes](https://presentations.juliobiason.me/filosofando-testes.html). |
||||||
|
{% end %} |
||||||
|
|
||||||
|
Antes de começar, alguns avisos: |
||||||
|
|
||||||
|
1. **Eu sou não ortodoxo com relação a testes**. Com isso eu quero dizer que |
||||||
|
muitas das coisas que eu vou comentar aqui são exatamente contrárias do que |
||||||
|
todo mundo fala e da forma como muitos trabalham com testes. |
||||||
|
|
||||||
|
2. De forma alguma, considere esse conteúdo como regras. O que eu quero é que |
||||||
|
as pessoas parem de sair criando testes sem saber porque estão fazendo |
||||||
|
esses testes. |
||||||
|
|
||||||
|
3. Ainda, de forma alguma você precisa concordar com alguma coisa aqui. De |
||||||
|
novo, a ideia é parar para pensar no que está sendo testado antes de sair |
||||||
|
testando. |
||||||
|
|
||||||
|
Agenda de coisas que eu vou comentar: |
||||||
|
|
||||||
|
1. TDD no estilo Kent Beck; |
||||||
|
2. "Fast Tests, Slow Tests"; |
||||||
|
3. Explosão de Testes Lentos; |
||||||
|
4. Coverage; |
||||||
|
5. Mocking. |
||||||
|
|
||||||
|
## TDD no Estilo Kent Beck |
||||||
|
|
||||||
|
O que me levou a repensar a forma como eu escrevia testes foi um vídeo do Ian |
||||||
|
Cooper chamado ["TDD, where it all go wrong"](https://vimeo.com/68375232) |
||||||
|
("TDD, aonde é que a coisa deu errado"). No vídeo, Cooper coloca que o livro que |
||||||
|
Beck escreveu (que deu origem a toda a revolução do TDD) diz apenas duas |
||||||
|
coisas: |
||||||
|
|
||||||
|
1. Testes devem ser executados de forma isolada, nada mais, nada menos. |
||||||
|
2. Evite testar detalhes de implementação, teste comportamentos. |
||||||
|
|
||||||
|
O primeiro ponto é o que fala sobre "unit tests", significando "rodam de forma |
||||||
|
isolada", no sentido em que um teste não depende de outro. Dessa forma, "unit |
||||||
|
tests" seriam traduzidos como "testes unitários", não "testes de unidade" -- |
||||||
|
não há "unidade", o teste em si é uma unidade única que não depende de outras |
||||||
|
coisas. |
||||||
|
|
||||||
|
O segundo ponto é que deve ser testado o comportamento, não a implementação. |
||||||
|
Esse é um ponto que eu vejo falhar um bocado quando pensamos em testar todo e |
||||||
|
qualquer classe e/ou função: E se o comportamento esperado é a combinação de |
||||||
|
duas classes? Vale a pena escrever testes para as duas, sendo que a questão de |
||||||
|
separar em duas classes diferentes (ou duas funções diferentes) se referem ao |
||||||
|
mesmo comportamento? |
||||||
|
|
||||||
|
Ainda, outro questionamento sobre testar todas as funções e todas as classes: |
||||||
|
o que sabemos de uma aplicação são os canais de entrada -- que pode ser por um |
||||||
|
botão em uma interface gráfica, um texto digitado na linha de comando ou uma |
||||||
|
requisição web -- e os canais de saída; assim, o _comportamento_ esperado é |
||||||
|
"dado essa entrada pelo canal de entrada, quero ter essa saída", e qualquer |
||||||
|
coisa no meio é implementação. De novo, para fazer a transformação de uma |
||||||
|
entrada para uma saída específica, eu posso precisar de mais de uma função |
||||||
|
e/ou classe; se eu estiver testando cada uma das funções, eu estou realmente |
||||||
|
testando o comportamento ou a implementação? |
||||||
|
|
||||||
|
"Mas isso é muito parecido com BDD!", você deve estar pensando. Cooper coloca |
||||||
|
isso no vídeo acima: Como a ideia de "testar cada função/classe" se tornou a |
||||||
|
norma do TDD, a questão do comportamento teve que ser colocado em outro |
||||||
|
formato, o que deu origem ao ATDD (Acceptance-Test Driven Development, |
||||||
|
Desenvolvimento Guiado por Testes de Aceitação) e BDD (Behaviour Driven |
||||||
|
Development, Desenvolvimento Guiado por Comportamentos). |
||||||
|
|
||||||
|
Um exemplo de como testar comportamento: No Subreddit do Django, foi criada |
||||||
|
uma pergunta: [Devo Escrever Testes Para os Tipos Built-In do |
||||||
|
Django?](https://www.reddit.com/r/django/comments/5bearg/should_i_write_unit_tests_for_djangos_built_in/) |
||||||
|
A questão se resume ao seguinte: Sabendo que no Django eu tenho tipos |
||||||
|
definidos para meus dados no banco, e a partir dessas definições eu posso |
||||||
|
criar formulários para colocar nos meus templates e esses formulários também |
||||||
|
servem para validar os dados de entrada; assim, se eu defini que há um campo |
||||||
|
no meu banco chamado "Ano de nascimento" -- que só pode receber números |
||||||
|
inteiros -- e eu crio o formulário a partir do banco, coloco no meu template, |
||||||
|
recebo os dados de volta e o próprio Django vai garantir, pelo tipo do dado no |
||||||
|
banco, que o resultado é um número inteiro, eu ainda preciso escrever um |
||||||
|
teste para isso? |
||||||
|
|
||||||
|
A solução, no entanto, é dar um passo atrás e fazer a seguinte pergunta: _Por |
||||||
|
que_ o ano é um inteiro? Obviamente, porque anos são definidos como números e, |
||||||
|
portanto, o comportamento do campo foi definido bem antes do campo ser |
||||||
|
adicionado na tabela. Ainda, imagine que, por algum acidente do destino, eu |
||||||
|
precise guardar o ano como uma string[^1]; se o tipo foi alterado, o |
||||||
|
_comportamento_ vai ser alterado também? Provavelmente não. |
||||||
|
|
||||||
|
Quando eu ignorei que ano deve ser um número porque "o framework cuida disso |
||||||
|
pra mim", eu ignorei o comportamento esperado por culpa da implementação. |
||||||
|
|
||||||
|
De novo, "teste comportamentos, não implementação". |
||||||
|
|
||||||
|
<!-- testes gerenciador de alertas --> |
||||||
|
|
||||||
|
<!-- https://www.youtube.com/watch?v=RAxiiRPHS9k fast tests, slow tests --> |
||||||
|
<!-- teste models, views, controllers ... soa similar? --> |
||||||
|
<!-- "desenvolvedores podem testar suas alterações rapidamente" ... soa |
||||||
|
familiar? --> |
||||||
|
<!-- testes de aderência a arquitetura do projeto --> |
||||||
|
<!-- qual o valor para o usuário desses testes? --> |
||||||
|
|
||||||
|
<!-- testes lentos --> |
||||||
|
<!-- "testes de integração são lentos" --> |
||||||
|
<!-- suite de testes tem que dar a opção de testar apenas o comportamento |
||||||
|
alterado --> |
||||||
|
|
||||||
|
<!-- coverage --> |
||||||
|
<!-- 100% de cobertura é possível, removendo código --> |
||||||
|
<!-- testes de integração do gerenciador de alertas mostrou que tínhamos |
||||||
|
código morto --> |
||||||
|
<!-- exemplo validação nomes --> |
||||||
|
|
||||||
|
<!-- mocks --> |
||||||
|
<!-- "coisas externas" vs "coisas fora do nosso controle" --> |
||||||
|
|
||||||
|
[^1]: O porque vai ser uma string pode ser variado: por causa de um plugin de |
||||||
|
segurança, porque é feito um armazenamento num banco que não trabalha bem |
||||||
|
com inteiros, por causa de uma integração com sistema legado... |
Loading…
Reference in new issue