Tag: testes

Automatizando o acesso a uma página web a.k.a. como eu “burlei” um sistema de agendamentos online

Neste artigo mostrarei, de maneira resumida, como automatizar a navegação em uma página web para fins diversos.

Mais especificamente: como eu fiz para conseguir um horário melhor num sistema de agendamento online através de um pequeno código que verifica novos horários vagos automaticamente. 😀

Leia mais

O TDD está morto?

Test Driven Development (Desenvolvimento Orientado a Testes) é uma metodologia de desenvolvimento de software bastante popular, que é centrada (adivinhe) em testes.

No entanto, apesar de muito propagada, há várias críticas a essa metodologia por profissionais e pensadores de destaque.

Por esses dias rolou uma conversa bastante interessante sobre o assunto entre, nada mais nada menos, que Martin Fowler (que elaborou a ideia de Injeção de Dependência), Kent Beck (idealizador do TDD e do framework JUnit) e David Heinemeier Hansson (criador do Ruby on Rails).

Vamos analisar os pontos altos da conversa nos próximos tópicos. Mas antes, uma breve revisão sobre TDD…

Como o TDD funciona

Ao contrário do processo “tradicional” de desenvolvimento que envolve Especificação, Implementação e Testes, o TDD começa pelos testes em cima das interfaces do sistema.

A princípio todos os testes irão falhar. O objetivo do desenvolvedor é fazer com que eles sejam executados com sucesso. Em tese, isso garante o foco no que realmente deve ser feito e fornece uma medida mais real do progresso.

Os testes utilizados no TDD geralmente são Testes Unitários. Ambos os conceitos estão muito relacionados, mas não são sinônimos.

Testes Unitários

Testes Unitários são aqueles onde uma única função ou método do sistema é testada de cada vez. Em geral esses testes são automatizados.

Isso pode parecer simples, mas implica em que a rotina em questão não pode depender de outras rotinas, caso contrário o teste é sujeito a falhas de terceiros.

Dessa maneira, a fim de testar as rotinas que naturalmente possuem dependências de outras classes e métodos, de recursos como bancos de dados ou arquivos e outros, os desenvolvedores criam simulações desses elementos para que a rotina a ser testada funcione, porém não dependa diretamente desses outros elementos durante o teste.

Este é o conceito de mock, isto é, algo que imita o elemento original.

Por exemplo, um método que normalmente retorna um valor do banco de dados poderia ser substituído por um método que retorna um valor fixo num teste em particular. Cada linguagem, framework e ferramenta deve prover formas adequadas para se fazer isso, sendo inclusive uma forma de medir a qualidade da mesma.

Benefícios desta abordagem

Escrever os testes antecipadamente e então desenvolver com base neles traz várias vantagens:

  • Melhora o entendimento dos requisitos, já que para escrever o teste é necessário que o desenvolver entenda logo de início o que o sistema deve fazer.
  • Provê um objetivo claro a ser alcançado durante o desenvolvimento, isto é, fazer os testes executarem sem erros.
  • Aumenta a visibilidade do que está ou não pronto, já que os testes servem como indicadores mais confiáveis do que algo que está implementado e “só falta testar”.
  • A automação dos testes possibilita a Integração Contínua com certa garantia do funcionamento do sistema, já que todos os testes unitários podem ser reexecutados como testes de regressão sempre que necessário.

Desvantagens

Como regra geral, tudo que traz vantagens tem seus pontos negativos. Sempre temos que tomar cuidado com certos “evangelistas” que irão fazer uma tecnologia ou metodologia parecer não ter um lado negativo ou impacto zero.

Isso se aplica também ao TDD. Vejamos algumas considerações sobre ele:

Aumento do esforço

Será necessário gastar muito tempo para criar os testes e todos os mocks, além da infraestrutura de Integração Contínua. É claro que isso pode ser visto como um investimento em qualidade ao invés de uma pena a ser paga, mas na prática nem todos podem se dar a esse luxo.

Aumento da complexidade

A arquitetura acaba sempre mais complexa, usando muita Injeção de Dependências e outras técnicas de desacoplamento.

Mudanças nos requisitos são muito mais custosas, já que além do código do sistema em si, todos os testes e mocks envolvidos deverão ser reescritos.

Se o arquiteto de software não souber o que está fazendo, a arquitetura do sistema pode acabar cheia de gambiarras.

Intrusividade no código principal

Em muitas situações, será necessário programar de uma forma específica para que um certo código possa ser testado.

A arquitetura da aplicação muitas vezes acaba sendo então afetada por elementos e fatores que, de outra forma, não fariam parte de sua natureza.

Enfim, o design da aplicação acaba sendo influenciado pelos testes, o que nem sempre é desejável, já que é mais uma coisa a ser levada em conta e pode desviar o foco do arquiteto.

Testar tudo é quase impossível

Muitas tecnologias são difíceis de testar com testes unitários ou mesmo com testes de integração.

Pense na interface do usuário, por exemplo. Quantas delas possibilitam a execução da interface de forma “desacoplada”? Existem ferramentas como o Selenium para sistemas web, mas com elas também vêm diversas outras limitações e dependências.

O TDD está morto?

Vimos que todo benefício traz um custo associado. É este todo o ponto da discussão sobre o TDD estar morto. O custo de testar tudo e criar os mocks necessários para todos os testes vale a pena? Vejamos alguns comentários a seguir.

David Hansson inicia a argumentação dizendo que muitas pessoas não conseguem trabalhar com TDD porque não faz muito sentido para determinados tipos de trabalho. Ele toca no ponto da necessidade de se ter muitos mocks e que isso acaba tornando a codificação difícil.

Kent Beck aproveita o gancho e relata de casos onde a equipe não conseguia refatorar o código porque os testes estavam tão acoplados com a implementação, com dois ou três níveis de mocks, que qualquer alteração afetava inúmeros deles. Ele também afirma que há cenários onde o TDD se aplica melhor, por exemplo, quando os requisitos do software a ser desenvolvido são claros o suficiente para escrevê-los em testes diretamente. Por outro lado, há situações onde simplesmente o desenvolvedor vai descobrindo aos poucos aquilo que deve fazer. Isso é recorrente na criação de frameworks ou algoritmos que precisam analisar dados não estruturados.

Fowler argumenta ainda que a maior vantagem do TDD é um código que consegue testar a si mesmo. Só isso poderia também ser alcançado de outras formas. Ele também comenta sobre aqueles que dizem que “você não está fazendo teste unitário de verdade” porque existem algumas dependências durante o teste. Segundo ele, o importante não é a pureza de uma definição, mas que o teste funcione e seja útil, até porque há variações da definição de testes unitários.

Conclusões

É importante entender que TDD não é a “bala de prata” do desenvolvimento de software. Ele traz consigo vários desafios e problemas em potencial, principalmente se não há pessoas suficientemente experientes na equipe.

Testar é essencial, mas para cada projeto podem haver diferentes formas de testes, cujo busto-benefício deve ser avaliado para cada caso.

O teste não deve ser um objetivo em si mesmo, ainda que adotemos o TDD. Ele apenas tem o objetivo de garantir que estamos entregando o que o cliente pediu.

O tempo e o esforço que são investidos em testes também devem ser avaliado. Isso afeta diretamente a qualidade, mas a qualidade tem o seu preço. Quem irá pagar o preço da qualidade?

Quanto a testes unitários, não devemos buscar toda a pureza possível. O importante é que ele teste, ou seja, funcione para aquela situação específica.

Concluindo, o conselho em geral é: use TDD com moderação.


Para quem consegue entender Inglês, aqui vai o link do Hangout com essas grandes personalizados:

Is TDD dead?

Quanto tempo gastar com testes?

software-testing

Ao planejar um projeto, quanto tempo deve ser reservado para os testes? Não seria suficiente calcular uma porcentagem em relação ao tempo estimado para desenvolvimento? É preciso mesmo planejar isso?

No silver bullets

Não existe um método mágico e correto para estimar o tempo gasto com testes, assim como não há uma solução mágica para o problema da estimação de software.

A verdade é que mesmo especialista em testes sugerem chutar! A princípio, use um fator mágico arbitrário para determinar o tempo reservado para testes. No decorrer do projeto, ajuste a proporção conforme sua produtividade e o nível de qualidade exigido.

Valores “mágicos”

Brooks, autor do famoso livro The Mythical Man-Month, descreve no capítulo 2 sua “regra” para planejar uma tarefa:

  • 30% para planejamento
  • 20% para codificação
  • 25% para testes de componentes e testes antecipados de sistema
  • 25% para testes de sistema e de integração geral

Os valores originais estão em frações, mas adaptei para porcentagem para facilitar o entendimento. Note que os testes ocupam metade do tempo de desenvolvimento (50%), o equivalente a 2,5 vezes o tempo de codificação.

Tenho notado em minhas atividades individuais que o tempo de testes varia de 1 a 1,5 vezes o tempo de desenvolvimento, incluindo testes unitários efetivos antes da codificação cobrindo cenários com valores limite e excepcionais. Além disso, é necessário mais 1 a 1,5 vezes o tempo de desenvolvimento para testes de integração após a codificação individual.

Em resumo, posso dizer que o tempo total de testes, quando feitos adequadamente, varia entre 2 a 3 vezes o tempo de desenvolvimento. Minha observação pessoal corresponde às observações de Brooks.

Pressa: e se não testarmos?

A dura realidade é que nem todos se dão ao luxo de separar tempo suficiente para testar. O termo “luxo” aqui foi usado com um pouco de ironia, porque a verdade é que, em última análise, isso é uma questão cultural da empresa e do indivíduo. Não, não tem tanto a ver com o cliente, porque a qualidade do sistema não é responsabilidade dele.

É responsabilidade dos profissionais de TI fazer o que for necessário para garantir o sucesso do projeto de software para o bem da própria empresa e do cliente, mesmo que as demais áreas não compreendam isso. Se a qualidade exigida não for financeiramente viável, então o projeto como um todo não está bem formulado ou simplesmente deve ser arquivado.

Tenho algumas considerações importantes que talvez possam ajudar:

  1. O tempo investido em testes unitários antes da codificação efetivamente diminui o tempo de codificação, já que o desenvolvedor tem que considerar cuidadosamente as entradas e saídas. De outra forma, as pessoas tendem a iniciar a codificação de um módulo do programa sem a noção do todo e precisa constantemente revisar o que já fizeram ao se deparar com novos detalhes e requisitos.

  2. Criar testes antes da codificação pode ajudar muito no entendimento do problema, já que o desenvolvedor precisa entender os requisitos para validar os resultados através de asserções.

  3. Em projetos “legados”, que não possuem testes unitários ou automatizados, o tempo gasto com correções tende ao infinito, pois a taxa de erros nunca estabiliza. Sem testes, não podemos mensurar corretamente o impacto das alterações e correções. A cada passo para frente, damos dois para trás. Uma correção pontual pode causar efeitos colaterais não esperados em outras funcionalidades.

  4. Uma premissa falsa que muitos tomam por verdadeira é que se as partes de um sistema forem implementadas corretamente, então não haverá problemas ao juntá-las no final. Isso é sempre um erro. Mesmo grandes desenvolvedores que sempre criam código de qualidade não podem evitar testes de integração, de sistema e de aceitação.

  5. Quanto mais tarde um erro for descoberto, maior o custo para corrigi-lo. Quanto antes os testes entrarem em ação, mais cedo eles podem contribuir para encontrar esses erros e problemas em potencial. Considere a imagem abaixo, extraída do artigo The Incredible Rate of Diminishing Returns of Fixing Software Bugs:

cost-fix-project

Um tempo bem gasto com testes permitirá aos desenvolvedores terem um horizonte visível do que é esperado do sistema.

Sobre estimação de software

Em minha pós-graduação, desenvolvi uma monografia sobre estimação de software. No momento em que escolhi este tema, acreditava que iria encontrar um método mágico para determinar o tempo das atividades de um projeto. Obviamente, logo que comecei a pesquisa percebi que havia acreditado num grande engodo.

Um dos livros mais interessantes de minha bibliografia foi Software Estimation: Demystifying the Black Art de Steve McConnell, cujo título já esclarece muito sobre a essência das estimativas: elas são tentativas de prever o futuro. Estimativas são chutes, simples assim.

A consequência disso é que não existe, e nunca existirá, uma regra definitiva para estimação das atividades de desenvolvimento de software. Na verdade, métodos “matemáticos” de estimação (COCOMO, Function Point) acabam confundindo seus usuários no sentido de que estes acreditam que o resultado terá acurácia garantida, afinal há todas aquelas fórmulas matemáticas complicadas. Aliás, é comum confundirmos acurácia (resultado bom) com precisão (nível de detalhe, casas decimais), mas uma estimativa pode ser muito precisa, porém longe da realidade.

Em decorrência disso, as metodologias ágeis não usam valores absolutos para estimar, como horas e dias, mas story points (pontos de história), que são grandezas relativas que variam de acordo com a equipe, o projeto e maturidade do desenvolvedor.

Os métodos mais modernos também não buscam precisão, isto é, estimar em muito detalhe (horas, por exemplo), já que frequentemente isso diminui a acurácia. Estimar em dias, semanas ou, em alguns casos, meses pode parecer “bruto” demais, porém os resultados são mais realistas. Não concorda? Então pense: quão confiável é uma estimativa de 370 dias e 4 horas? Sinceramente, alguém pode prever tudo o que ocorrerá em praticamente um ano?

Então, cuidado com soluções mágicas que podem tentar lhe vender. Embora algumas técnicas de estimação pareçam melhores, na verdade ninguém pode afirmar absolutamente que determinado método é melhor. Fazer isto seria o mesmo que afirmar que você tem um método para jogar na loteria melhor que outras pessoas, sendo o resultado simplesmente aleatório.

Leia algumas conclusões adicionais sobre estimação de software no artigo Reflexões sobre a natureza do software e das estimativas de software.

Mas isso significa que devemos sempre “chutar” durante o planejamento? Claro que não! Podemos chutar com estilo. 😉

Estimação por analogia

Estimar é prever o futuro. Porém se as atividades de desenvolvimento de um novo projeto são análogas a experiências anteriores, os envolvidos podem ter uma boa ideia do esforço necessário para executá-las.

Isso não acaba com os imprevistos, nem garante um cronograma em dia, mas resultados empíricos mostram que uma técnica adequada e a experiência do planejador contribuem para melhores estimativas.

Uma outra abordagem indicada pelos autores e especialistas em estimação é medir e armazenar a produtividade da equipe para então projetar os resultados futuros. Individualmente, este é um dos principais objetivos do PSP (Personal Software Process ou Processo de Software Pessoal). Um dos pilares do Scrum, a Inspeção, deve permitir acompanhar o progresso e a produtividade da equipe.

Embora a estimação, tanto dos testes quanto das demais atividades do desenvolvimento de software, seja uma tarefa mais de intuição do que um processo científico, em geral observa-se uma melhora na qualidade das estimativas com o uso sadio de dados históricos e analogia das atividades novas com as já executadas.

Uma aplicação prática disso para os teste é muito simples. Imagine um cenário onde você entregou a versão inicial do produto e irá partir para a segunda fase de um projeto de N fases. Se você mediu o tempo gasto com os testes na primeira fase, calcule o desvio com relação ao tempo planejado e aplique-o no planejamento da segunda fase. O processo deve ser repetido a cada fase do projeto.

Note que isso é compatível com o conceito do Cone da Incerteza, o qual afirma que as estimativas são melhores a cada fase do projeto. Considere a imagem abaixo, extraído deste artigo):

cone-da-incerteza

Na medida em que o projeto se desenrola, em tese, podemos analisar com mais certeza o horizonte de sua conclusão. No entanto, a curva não é a mesma para todo projeto e empresa. Os autores afirmam que a curva acima é o caso ótimo e se não houver gerenciamento adequado a incerteza continuará grande mesmo na data de término do projeto!

Estimação em faixas

Isso nos leva a outro conceito: estimar em faixas de valores. Podemos usar esta técnica para estimar o melhor e o pior caso para uma determinada atividade. A diferença entre esses casos corresponde à incerteza atual.

Tenho usado faixas de valores para trabalhos como consultor independente e tem funcionado bem, principalmente em atividades que envolvem algo novo para mim, portanto não sei ao certo quanto tempo será necessário de pesquisa, desenvolvimento e testes.

Num exemplo simples, posso responder que a atividade poderá levar de 8 a 12 horas. Se tudo correr bem, pode ser até até menos. Se tudo der errado, talvez eu tenha que negociar horas a mais. Na maioria das vezes consigo acertar e cobro algo dentro da faixa.

Isso também permite ajustes relacionados a reuniões e telefonemas. Se em um projeto passei duas horas no telefone com o cliente, esse tempo é incluído no tempo da atividade. Se o cliente especificou bem a tarefa e não foi necessário muito tempo de análise, bom para ele, vai pagar menos.

É claro que isso envolve uma relação de confiança por parte dos clientes, mas a ética do profissional cedo ou tarde torna-se patente.

Estimativa ou compromisso?

Outro erro comum no dia-a-dia é confundirmos estimativas com compromisso por parte dos desenvolvedores.

Imagine que uma empresa estima os testes com uma regra mágica de 50% do tempo de desenvolvimento. Os desenvolvedores percebem que estão gastando muito mais do que isso.

Uma reação comum para não “atrasar o cronograma” é simplesmente escrever menos testes do que o planejado, como se a estimativa inicial fosse uma obrigação a ser seguida.

O correto seria revisar a estimativa inicial e não tentar se ajustar a ela. O problema é que na prática…

Frequentemente falta aos gerentes de software firmeza de fazer o pessoal esperar por um bom produto (Brooks)

A qualidade é um fator determinante

Usei a citação acima em um artigo sobre o Triângulo de Ferro que escrevi há algum tempo. Este conceito é importante pois demonstra que a qualidade possui uma relação direta e proporcional com o tempo despendido no projeto.

Isso implica em afirmar que mais qualidade exige mais tempo. Por isso, a decisão de investir em mais ou menos testes no início do projeto influenciará diretamente na qualidade final do produto.

Diminuindo o tempo despendido com testes sem prejudicar a qualidade

O título parece contradizer o que acabei de dizer. Mas, se tomarmos o conceito de separação entre atividades essenciais e acidentais do desenvolvimento como faz Brooks em No Silver Bullets, podemos dizer que, embora não haja como evitar os testes sem diminuir a qualidade, podemos diminuir as dificuldades acidentais da criação deles.

Isso pode ser alcançado de algumas formas:

  • Treinando a equipe para melhorar a produtividade
  • Usando ferramentas mais adequadas que facilitem a criação e execução dos testes
  • Investindo na automação
  • Usando tecnologias (frameworks, plataformas) que facilitem os testes

Enfim, podemos ser mais produtivos fazendo mais testes e menos trabalho repetitivo manual.


Este artigo foi baseado na minha resposta no StackOverflow em Português!

Creative Commons O blog State of the Art de Luiz Ricardo é licenciado sob uma Licença Creative Commons. Copie, compartihe e modifique, apenas cite a fonte.