Introdução: O Dilema do Polling
Em testes de software modernos, especialmente na automação de interfaces de usuário (UI) ou testes de ponta-a-ponta (E2E), raramente os eventos acontecem instantaneamente. Um botão pode demorar 2 segundos para aparecer após um clique, um relatório pode levar 30 segundos para ser gerado, ou uma API pode demorar um tempo variável para responder.
Como nossos testes não têm “bola de cristal”, eles precisam esperar. No vocabulário técnico, isso é feito através de Polling : o teste verifica se uma condição é verdadeira (ex: “o botão está visível?”), se não for, ele dorme por um tempo, e tenta novamente, repetindo até o sucesso ou até estourar um tempo limite (Timeout).
O Problema com as Esperas Tradicionais
As estratégias de polling mais comuns sofrem de um compromisso doloroso entre velocidade do teste e carga no sistema.
- Espera Fixa (Static Wait): O teste verifica a cada intervalo exato (ex: a cada 1 segundo): Se o evento acontece 100ms após a última verificação, o teste perde 900ms esperando desnecessariamente. Multiplique isso por centenas de testes e sua pipeline torna-se lenta.
- Aumento Exponencial (Exponential Backoff): O teste dobra o tempo de espera a cada tentativa (ex: $1s, 2s, 4s, 8s…$). É ótimo para SRE/Redes para evitar sobrecarga (efeito manada), mas em testes, se o evento demorar um pouco mais, o próximo intervalo de espera pode ser giganticamente maior que o necessário, atrasando o feedback final.
O Conceito: Espera em Escala Logarítmica
Propomos uma abordagem diferente para cenários onde sabemos que um evento vai demorar , mas queremos capturá-lo no exato momento em que ele ocorrer.
A ideia central é baseada na percepção humana de tempo em processos longos: quanto mais tempo esperamos, maior é a probabilidade de que o evento esteja prestes a acontecer.
Em vez de desacelerar (como na exponencial) ou manter o passo (como na fixa), a Espera Logarítmica Acelera.
- No Início: Quando a chance de sucesso é quase zero, fazemos pollings bem espaçados (ex: a cada 5 segundos) para economizar recursos.
- Com o Passar do Tempo: À medida que o tempo avança e a chance de sucesso aumenta, diminuímos o intervalo entre as verificações (ex: $4s, 3s, 2s, 1s, 0.5s$), tornando a busca cada vez mais agressiva.
A Fórmula Explicada (Sem Dor)
A fórmula que define o intervalo de sono é:
(Com uma trava de segurança para não ser menor que um valor mínimo, o Floor).
Vamos torná-la didática:
Pense na fórmula como uma Escada Rolante que Desce :
- Initial: É o intervalo mais longo que você quer no começo. Ex: começar esperando 5 segundos entre cada verificação.
- Tentativa (Os Degraus): A cada vez que o teste falha, ele desce um degrau. Quanto maior o número da tentativa ($1, 2, 3…$), mais baixo na escada você está.
- Tentativa + 1 (O Freio do Logaritmo): Usamos o logaritmo porque ele cresce devagar. Isso garante que a aceleração não seja absurda nas primeiras tentativas, dando tempo ao sistema.
- k (A Velocidade da Escada): É uma constante que calculamos automaticamente baseada em quão rápido você quer chegar ao final (o Floor).
Visualizando a Diferença
5 |*
4 | *
3 | *
2 | *
1 | *
0 | *
-1 | **
-2 | ***
-3 | ****
+--------------------------------
0 2 4 6 8 10 20
| Tentativa (`x`) | Intervalo (`y`) |
| --------------- | --------------- |
| 0 | 5.00 |
| 1 | 3.27 |
| 2 | 2.25 |
| 3 | 1.53 |
| 5 | 0.52 |
| 6 | 0.14 |
| 7 | -0.20 |
| 10 | -0.99 |
| 20 | -2.61 |
Observação importante:
a função começa a produzir valores negativos após algumas tentativas, porque o logaritmo continua crescendo.
Para evitar isso em cenários reais (retry/backoff/cooldown), normalmente usa-se um piso mínimo:
Intervalo = max(Minimo, Initial - k * log(Tentativa + 1))
Exemplo de Uso em Python
import pytest
import time
import math
from typing import Callable
class LogarithmicWait:
def __init__ (
self,
timeout: float = 30.0,
initial_interval: float = 3.0,
floor_interval: float = 0.5,
acceleration_point: int = 15
) -> None:
"""
Initializes the logarithmic waiter.
:param timeout: Maximum time to wait in seconds.
:param initial_interval: Starting delay between polls.
:param floor_interval: Minimum delay between polls (maximum speed).
:param acceleration_point: How many attempts until we reach the floor_interval.
"""
self.timeout = timeout
self.initial_interval = initial_interval
self.floor_interval = floor_interval
# Calculate the decay constant based on the desired acceleration curve
# (Initial - Floor) / log(N + 1)
self.k = (initial_interval - floor_interval) / math.log(acceleration_point + 1)
def until(self, condition: Callable[[], bool]) -> bool:
"""
Polls the condition function until it returns True.
"""
start_time = time.monotonic()
attempt = 1
while time.monotonic() - start_time < self.timeout:
if condition():
return True
# Decay logic: interval decreases as 'attempt' increases
interval = max(
self.floor_interval,
self.initial_interval - self.k * math.log(attempt + 1)
)
time.sleep(interval)
attempt += 1
Conclusão: Benefícios e Limitações
Onde ela tem mais sucesso?
- Processos Assíncronos Longos: Geração de PDFs pesados, processamento de vídeos, backups, ou provisionamento de infraestrutura em nuvem.
- Testes de CI/CD em Escala: Em pipelines com milhares de testes parallelism, a economia de recursos nos primeiros segundos de cada teste pode reduzir drasticamente o custo e a carga da infraestrutura de teste.
Benefícios
- Feedback Snappy: O teste termina quase imediatamente após o sucesso real, eliminando o “tempo morto” do polling fixo.
- Carga Eficiente: Não bombardeia o sistema desnecessariamente no início.
Limitações
- Eventos Rápidos: Para elementos de UI que aparecem em menos de 1 segundo, esta abordagem é desnecessariamente complexa; a espera fixa tradicional do Selenium/Playwright é melhor.
- Complexidade de Configuração: Requer que o QA entenda a natureza do processo para configurar bem o Initial e o Floor. Se o Initial for muito alto, você pode perder tempo no começo de um evento que poderia ter sido rápido.
A Espera Logarítmica não é uma bala de prata, mas é uma ferramenta de engenharia refinada para QAs que buscam otimização de alto nível e precisão em cenários complexos.
Powered by Claude e Gemini

Top comments (0)