Testes de Unidade são realmente nescessários?

Massa @leandrosp valeu por compartilhar a visão. Quando disse na verdade que as pessoas chamam de “bosta” quem não faz testes, estou falando de um senso comum que foi criado pela comunidade e que muitas vezes é propagado por grandes nomes da comunidade, não falei da Thread em si.

As vezes, perde-se mesmo apenas o senso de que nem todo código está nascendo hoje ou possui boas práticas, na verdade é exatamente o contrário.

Mas como disse, isso não me afeta mais… :stuck_out_tongue:

No mais, queria entender o que vocês chamam de testes de UI? Testar a interface de usuário com um Selenium da vida?

Continuem com a discussão, o nível está excelente e agregando bastante. Abraços a todos :wink:

I/O dificulta os testes porque você não tem controle sobre o que acontece durante I/O.

Chamadas ao relógio (new Date()), sistema de arquivos, gerador de números aleatórios, camada de rede, tudo isso, sim, pode dificultar os testes porque seu código deixa de ser determinístico, ou seja, retorna diferentes resultados para os mesmos parâmetros de entrada. Por isso, é importante você segregar código que faz i/o de código que não faz i/o. Encapsular essas chamadas atrás de classes que você pode substituir em tempo de execução é como geralmente resolvo o problema.

Armazenar estado em objetos não dificulta testes unitários, na minha experiência.

Exato, os analistas de qualidade usam Selenium webdriver por exemplo para automatizar testes de aceitação do que de fato será utilizado pelo usuário final. Não é um “conceito” novo, nada mais é do que automatizar o que era feito manualmente antes de forma repetitiva.

Sim, vc pode segregar o que conhece. Mas não o que está escondido.

Pra colocar um programa em determinado estado precisa criar objetos falsos, configura-los de uma maneira que é difícil de fazer de maneira automática pra cada teste case.

Pra mim parece bem complicado de se aplicar num projeto em larga escala. Não sei a opinião dos demais.

Se eu estou te entendendo, se o problema é devido ao fato de em OO não ser natural criar “funções puras” como nas linguagens funcionais, pelo menos até hoje consegui resolver isto aplicando single responsibility nas classes e componentes.

Ao invés de criar uma classe com método que faz um monte de coisa, criando mocks muito deep e tornando difícil fazer track do estado, eu tento ir dividindo em classes (que fazem sentido) até chegar num ponto onde uma classe XPTO é responsável por fazer algo mais complexo (ex. I/O) e, neste caso, o mock passa a ser mais simples pois toda a stack que ficou pra trás já está testada.

Até hoje consegui resolver assim nos modelos de domínio com os quais trabalhei. Talvez você esteja se referindo a um modelo complexo demais que torna mesmo impraticável testar. Vai saber…

1 curtida

O @leandronsp levantou uma questão bastante pertinente ao desenvolvimento e aos testes em si: a complexidade.
É sabido que uma das premissas menos faladas e utiizads da OO é a responsabilidade única das classes (lembro-me de um professor da faculdade que exigia, nos diagramas de classe, uma quarta divisão onde fosse definida a responsabildiade da classe).
Por que digo isso? Em sistemas legados (e muitos sistemas contemporâneos) é comum encontrarmos classe estilo canivete suíço: cobra escanteio, cabeceia e corre pro gol para defender. Fazem de tudo e, cada método, pode ter milhares de linhas, tornando-a um gigantesco monumento dos incorrigíveis comentários:

/* Não mexer que funciona assim */

É óbvio que coisas assim vão refletir em outros pontos, como a coesão e a acoplamento, em efeito cascata, vai atingir a criação de testes (e toda a funcionalidade do sistema).
Assim sendo, voltamos ao ponto de que temos erros mais profundos que afetam toda a cadeia: como alguém que mal sabe ou não quer saber como criar um código melhor vai ter visão para criar ou mesmo utilizar testes?

1 curtida

O problema é a falta de transparência referencial.

Meu ponto é que o sistema em produção ainda pode entrar num estado ruim devido algum estado interno escondido (e por ser escondido, não foi possível testar durante o desenvolvimento).

E outra, se vc precisa criar 50 classes pra modelar algo simples, estará aumentando as chances de introduzir bugs.

Enfim, tudo isso não é um problema se o objetivo é fazer um botão produzir um efeito glow quando clicado, mas para software crítico melhor evitar OO (pelo menos a versão mainstream que não possui transparência referencial, OO no estilo do Erlang/Akka ok).

2 curtidas

E para raciocínio crítico, melhor evitar as generalizações. :slight_smile:

2 curtidas

Mas não é uma generalização.

Software critico: classe especifica de software

Mainstream OO: versão especifica de OO, mais voltado para os interesses do mercado.

Bom, como a maior parte do software de missão crítica roda em linguagens imperativas, impuras e profanas e o mundo ainda não acabou, acho que você fez uma baita generalização.

1 curtida

Poderia citar um exemplo de software crítico feito em OO?

Defina software crítico.

1 curtida

Acho que já vi essa frase há muitos anos aqui no GUJ, não me lembro quando nem o autor. Parafraseando:

"é possível fazer qualquer coisa em qualquer linguagem que seja turing complete."

E isso vale para “software crítico”.
Encapsulando bem o comportamento dos objetos dá pra evitar o problema que você se refere à transparência referencial. Claro que, em certos tipos de linguagens funcionais é mais óbvio construir tais tipos de software devido à natureza funcional.

Por exemplo em Erlang/Elixir, você consegue ter tolerância a falhas e resiliência com o uso de supervisors e gen servers. Em Clojure, o Datomic com sua natureza imutável garante que dados não são perdidos.

Enfim, em certas linguagens ou tecnologias talvez seja menos custoso implementar tal tipo de software, mas não é impossível com OO se for bem feito, e claro, voltando à origem da thread, com testes escritos como devem ser.