Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@klauswuestefeld
Created August 30, 2011 14:19
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save klauswuestefeld/1181000 to your computer and use it in GitHub Desktop.
Save klauswuestefeld/1181000 to your computer and use it in GitHub Desktop.
Transcendendo Teste Unitário?
É bem útil conceituar forte e explicitamente a diferença entre Resolver/Eliminar um problema
e Contornar/Gerenciar um problema.
Exemplo de Problema: "Tá um inferno gerenciar os bugs do nosso sistema."
Contornar/Gerenciar: "Vamos subir um bug tracking tipo Jira/BugZilla da vida!"
Resolver/Eliminar: "Vamos melhorar a qualidade do código até reduzir em 98% o surgimento de
bugs."
Pensei nisso pq estou experimentando partir de um código praticamente intestável (uma engine
de continuous deployment q depende de http, filesystem e execução de processos externos (o
problema)) para um código tão simples (código das partes e código de cola extremamente
simples (a solução)) que mal precisa ser testado, em vez de recorrer pesadamente a mocks e
testes complexos (o contorno).
@klauswuestefeld
Copy link
Author

Simploy é o nome da engine de deploy contínuo q estou fazendo. Considero o seguinte código tão simples q não precisa ser testado unitariamente:

public class SimployMainLoop {

    public SimployMainLoop(Deployer deployer, Trigger trigger) {
        deployer.deployGoodBuild();
        while (true) {
            trigger.waitFor();
            deployer.deployNewBuild();
        }
    }

}

Pergunta: O código acima é útil?

Se for, ele é um passo na direção de um deploy engine que não precisa ser testado unitariamente.

@juanplopes
Copy link

Então códigos simples não precisam de teste? Duvido.

Se você escreve testes simplesmente como forma de resolver/evitar bugs, está caminhando na direção certa com o motivo errado. Testes explicitam propósito enquanto garantem comportamento. Incentivando, é claro, um design testável, que geralmente é um design menos acoplado. Não existe código simples demais pra isso.

Em suma: não existe código naturalmente intestável. O que existem são modelagens que dificultam o teste. Por isso que o TDD é tão importante.

@klauswuestefeld
Copy link
Author

Oi Juan,

Não está claro p vc o propósito do código acima?

@juanplopes
Copy link

Sim, está. Mas até quando estará? Um sistema é feito de integrações, e nem todos os contratos estão explicitos. Por exemplo, não sei se é o seu interesse, mas parece que você deseja que o worker faça a thread dormir por um certo tempo ao chamar o shouldDeployNextBuild. Se ela não fizer isso, seu loop pode consumir todos os recursos da máquina.

Os testes esmiúçam esses pequenos detalhes contratuais que temos em mente quando escrevemos o código, mas não são escritos explicitamente.

@klauswuestefeld
Copy link
Author

Interessante, Juan.

Dado seu último comentário, alterei o código p ficar mais simples. Veja se está bom ou se um teste unitário disso envolvendo threads, waits, relógio e o caramba realmente ajudariam o entendimento.

@klauswuestefeld
Copy link
Author

Atente q o experimento aqui é transcender o teste unitário, nao teste de integração (ainda).

@juanplopes
Copy link

No exemplo ficou bem simples e direto. Não precisa de grande aparato pra entender o que esse código faz.

Entretanto, acho que não é o caso geral. Existem coisas que são naturalmente difíceis de testar (código que depende de I/O, por exemplo) e se disciplinar a escrever testes unitários te ajuda a explicitar onde esses códigos existem e precisam ser isolados.

Além disso, classes com muitas responsabilidades acabam gerando uma explosão combinatória de testes, o que te ajuda a verificar violação de SRP.

O meu objetivo é dizer que testes unitários não devem ser vistos como obrigação, mas sim como uma ferramenta que te ajuda a fazer um design melhor. E de quebra, você acaba documentando o comportamento esperado do seu sistema. Não sei se gosto de defender essa transcendência.

@dannluciano
Copy link

Geral mente recebo muitas criticas sobre isto mas acredito que algumas features não precisam mesmo de teste. As vezes elas estão muito na cara. Muita gente fala, "mas vai que você se esquece de algo no código?", aew eu digo, "mas vai que você se esquece de algo no teste?"
Muita gente simplesmente acha que se os testes então verdinhos então tah tudo lindo, o que na pratica não é verdade. Escrever testes de qualidade (que realmente testam de verdade) é tão complexo com escrever código de qualidade (que funcione de verdade).

Um exemplo clássico são os CRUDs simples onde existem poucas ou nenhuma validações, e etc.

@rferraz
Copy link

rferraz commented Aug 31, 2011

A declaração que é possível transcender testes unitários trai uma falta de compreensão do que TDD fundamentalmente representa, mesmo no quesito de validação somente. TDD, como um ferramenta de design, é exatamente o que garantiria que a dita simplicidade de código corresponde com a realidade de sua execução. Invocando Donald Knuth, "Beware of bugs in the above code; I have only proved it correct, not tried it". Considere as declarações y = x++ e y = ++x. Ambas trivialmente simples e compreensível mas podem fazer a diferença entre um desastre e uma execução impecável. Além disso, compreensão imediata também é uma função de tempo e contexto.

Existem dois argumentos fundamentais contra a idéia apresentada.

Primeiro, o código acima está longe de ser tão difícil assim de testar. Ferramentas de deploy podem, sim, ser testadas. Basta um modelo BDD (como o Cucumber) com algum servidor auto-contido como um Jetty e uma aplicação simples e o teste nem precisa de mocks. Em circunstâncias especiais (que já presenciei) é possível testar para garantir a existência de um bug em particular, ou seja, testes nem sempre são o que parecem.

Segundo, há uma falta de compreensão também sobre o que é um teste unitário--testes que dependem de contextos isolados e que são autônomos por si próprios. O código acima pode não ser tratável via testes unitários diretamente, mas isso não quer em absoluto dizer que transcende os mesmos pela sua simplicidade--até porque existe tanto comportamento escondido no mesmo que a declaração continua inválida. E, como mencionado acima, ele é absolutamente tratável por outros tipos de testes.

Em resumo, para invocar outro sábio, "não há balas de prata".

@santagada
Copy link

A idéia geral é boa, existem alguns casos onde o código em si é tão simples que não precisa ser testado. Agora nesse caso especifico, provavelmente as outras duas funções que não tem implementação vão precisar de muito teste.

@juanplopes
Copy link

juanplopes commented Sep 1, 2011 via email

@klauswuestefeld
Copy link
Author

Não tem nenhum x++ nem y-- no código acima.

Consigo conceber unidades de código como um quicksort (talvez a galera do Haskell discorde) que precisam necessariamente de teste unitário. Mas quero propor q existem unidades, como a acima, q não precisam.

A unidade acima é útil? Precisa de testes >unitários< ou nao?

Se nao precisa, saberemos q não é 100% das unidades q precisam. Resta saber: qual a porcentagem q precisa? Mais de 50%? Menos? Qual sua opiniao?

Aos q forem argumentar no sentido de q o código acima precisa de teste >unitário<, postem aqui, por favor, o CÓDIGO de teste e digam no q exatamente ele ajuda.

@kalecser
Copy link

kalecser commented Sep 5, 2011

Me parece que o fato de caberem ou não testes unitários não é o ponto, o ponto é se precisa ou não de TDD.

Como é que eu vou saber se essa é a implementação mais simples que resolve o problema sem ter os testes que levaram a esse código?

@klauswuestefeld
Copy link
Author

Como saber se uma implementação é a mais simples mesmo tendo os testes?

@rferraz
Copy link

rferraz commented Sep 5, 2011

A questão não é se o código acima tem ou não x++ ou ++x. A questão é que não existe código simples demais que não mereça testes. Uma das poucas razões para não se realizar um teste é se o mesmo custa mais do que o valor que entrega--o que poderia, remotamente, ser o caso dos testes acima (novamente, eu advogaria o uso de testes de integração por motivos óbvios). Isso, por outro lado, não tem absolutamente nada a ver com transcender tests unitários. There ain't such a thing.

@kalecser
Copy link

kalecser commented Sep 5, 2011

Como saber se uma implementação é a mais simples mesmo tendo os testes?

Simplificando a implementação (se possível) e rodando os testes pra ver se eles continuam atendendo o requisito. Claro que isso só vale pra TDD.

Eu não consigo visualizar a classe SimployMainLoop como uma entidade isolada do sistema, ela depende de que os métodos da classe Worker não lancem exceptions por exemplo, usando TDD isso implica pelo menos um teste do tipo onServerNotAvailable_SimployWillContinueToRun. Pode ser que olhando esse teste fique claro que é mais simples deixar os métodos da classe worker jogarem Exceptions e tratar na SimployMainLoop.

@klauswuestefeld
Copy link
Author

kalecser, será q uma unidade só é válida quando ela é complicada o suficiente para justificar um teste unitário? Não estou falando de um código hipotético. Fiz a implementação do SaimployMainLoop e as classes q ela usa não jogam exception. Atualizei o código acima pq acabou até ficando mais simples.

rferraz, não realizar testes unitários pq entregam menos valor do q custam e usar apenas testes de integração, por definicao tem tudo a ver com transcender testes unitários.

@rferraz
Copy link

rferraz commented Sep 12, 2011

Eu acho que você está confundindo o significado da palavra transcender. A mesma implica na existência de conhecimento o bastante para dizer que os testes não são necessários porque você pode afirmar absolutamente que não há bugs no mesmo. A própria necessidade de testes invalida isso. Como eu disse anteriormente, o fato de que a melhor maneira de testar certo código é através do uso de testes de integração não implica transcendência.

@klauswuestefeld
Copy link
Author

Continuando a mesma discussão: https://gist.github.com/1215891

(rferraz, deletei meu ultimo draft de post, q ficava acima. mova essa sua resposta pra nova discussao, revisando-a, por favor)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment