Testes de Unidade são realmente nescessários?

Bom meu ponto anterior foi justamente por esse lado. Você talvez por ser o que tem costume de trabalhar, etc, acha que UI é secundário.

Mas isso no geral não é assim, é o que o usuário vê, pro usuário seu sistema pode ser perfeito por dentro mas se não atender o jeito que ele vai usar, ele simplesmente não vai comprar.

Interface com usuário ainda é mais critico quando o que você desenvolve é baseado em interação e a experiência do usuário como é o caso de desenvolver bibliotecas de UI (se não testar UI nesse caso, vai testar o que? hahah), coisas envolvendo gráficos, entrada do usuário, e que muitas vezes tem um aspecto subjetivo e estético.

Como falei há casos em que não só é caro, como é praticamente impossível testar de forma automatizada pelo fato de que bolar o teste em si não é trivial. Por isso minha conclusão é que TDD e BDD não atendem todos os casos, como querem prometer que fazem.

1 curtida

Escrever testes automatizados é muito caro pra quem trabalha com OO porque a maior parte do código é statefull, assim como a UI, e não pode ser testado de maneira confiável.

1 curtida

Não acho UI secundário de todo, mas existem features que podem ser testadas de forma mais barata utilizando outros métodos de teste. É questão de avaliar cada caso. Eu não disse em nenhum ponto desta thread que não testo UI at all.

Sim, isso é óbvio. Ninguém compra (ou ao menos não deveria comprar) algo que não atende as expectativas.

Com razão. Há casos que o custo de automatizar algo é muito alto, tem que avaliar alternativas mais baratas.

Pra testar um sistema stateful é fácil, vc o coloca num estado inicial e executa os testes desejados usando os parametros de entrada.

Depois é só rezar para os testes executem da mesma forma, toda vez para os mesmos parametros de entrada, e algum estado interno escondido não faz com que uma sequencia de eventos de entrada coloque o sistema num estado ruim, pq senão aí lascou :confused:

Isso é fato. Cada empresa tem seu cenário, geralmente em fábrica/consultoria, pela rotatividade de jovens, tem mais preocupação com teste do código que os próprios programadores usam, do que a funcionalidade para o usuário final. O ritmo desse ambiente exige diversas siglas para o desenvolvimento não virar o caos, além de se preocuparem mais em prever situações por não ter o feedback natural do cliente, diferente quando se está diretamente na atividade fim, onde você já faz parte do negócio. Claro que isso não é regra, tem empresas de TI que são pagas para se preocupar em manter a qualidade do que de fato vai ser usado.

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.