Regressão visual: por que comparar pixel a pixel deixa passar mudanças reais

Regressão visual: por que comparar pixel a pixel deixa passar mudanças reais

A maioria das ferramentas open source de regressão visual — BackstopJS, Wraith ou as verificações de capturas do Playwright e do Cypress — baseia-se na mesma ideia: tirar uma captura de tela antes e depois e contar os pixels que mudaram. O motor é quase sempre o mesmo: comparar as duas imagens pixel a pixel.

É simples, robusto e perfeito para detectar uma quebra grande de layout. Mas essa abordagem tem um compromisso estrutural que pouca gente mede. Nós medimos, com números na mão — e em casos escolhidos para expô-lo, o resultado é claro.

Dois ajustes que não se devem confundir

A confusão está em todo o lado, então vamos esclarecer. Esse tipo de ferramenta tem dois ajustes distintos:

  1. A tolerância por pixel: a partir de que diferença de cor se considera que um pixel «realmente» mudou. É um controle de sensibilidade à cor, aplicado pixel a pixel.
  2. O limiar de decisão: depois de contados os pixels diferentes, olha-se que porcentagem da imagem mudou para decidir se o teste passa ou falha. A ferramenta de referência BackstopJS o fixa por padrão em 0,1% dos pixels.

O nosso teste segue exatamente essa lógica: contamos os pixels diferentes (ferramenta na sua sensibilidade padrão), e depois aplicamos o limiar de decisão de 0,1% para dizer passa ou falha.

O dilema do limiar

Esse limiar em porcentagem prende a comparação da imagem inteira num compromisso que ela não pode vencer:

  • Com um limiar em porcentagem (ajuste padrão, 0,1%) → você deixa passar as pequenas mudanças reais. Um botão que muda de cor, uma borda que se arredonda, um estado de célula «OK» → «ERRO»: tudo isso pesa uma fração minúscula dos pixels. Abaixo de 0,1%, o teste fica verde mesmo que a página tenha mudado de forma visível. Uma mudança real passa despercebida.
  • Sem nenhuma tolerância (o outro extremo, o ajuste padrão do Playwright) → você falha ao menor pixel de diferença, incluindo o simples suavização das bordas — aqueles pixels semitransparentes na borda das letras e das formas, que variam ligeiramente de uma exibição para outra. O resultado: falsos alertas aos montes, e a equipe acaba ignorando a ferramenta.

As ferramentas reais oferecem salvaguardas — mascarar zonas, ignorar regiões, recortar. Funcionam, mas é preciso configurá-las com antecedência, à mão, zona a zona. A sensibilidade localizada não é automática.

O teste em condições reais

Comparámos, sobre exatamente a mesma página (renderizada por um navegador, congelada de forma reproduzível, largura de tela de 1280 px, captura da página inteira), duas abordagens:

  • Delta-QA: o nosso comparador, que compara elemento por elemento (associa os elementos entre as duas versões e só compara os pixels no nível mais fino);
  • a comparação da imagem inteira: comparamos as duas capturas pixel a pixel, e depois aplicamos o limiar de decisão de 0,1%.

Cinco casos, escolhidos para expor o ponto cego:

Caso Mudança Delta-QA (elemento por elemento) Comparação de imagem inteira (% de pixels alterados, veredito no limiar 0,1%)
Deslocamento um triângulo se move na página 2 sinais: desaparecido + apareceu no elemento 0,005% → NÃO DETECTADO
Reordenação cartões e menu reordenados 13 sinais localizados (quais cartões, quais itens) 0,63% → falha, mas bloco difuso, sem precisar nada
Mudança fina borda arredondada de 12 px a 30 px 1 sinal no cartão afetado 0,011% → NÃO DETECTADO
Cor localizada cabeçalho de um cartão verde → roxo o cabeçalho certo (intensidade 0,996) + 2 sinais pais fracos 5,03% → falha, mas bloco difuso, sem precisar nada
Célula de tabela estado de uma linha FAIL → WARN 2 sinais fortes nas linhas certas 0,036% → NÃO DETECTADO

Nestes cinco casos, três mudanças reais — um deslocamento de elemento, um arredondamento de borda, um estado de célula — ficam abaixo do limiar de decisão padrão de 0,1%. Uma ferramenta configurada por padrão declara-as «sem mudança». E no entanto são exatamente as regressões que um teste visual deveria detectar.

Para os dois casos que a comparação pixel a pixel «detecta» (reordenação, cor), ela só diz uma coisa: «X% dos pixels mudaram nalgum sítio». Nenhuma ideia de qual elemento, nem de que natureza. O Delta-QA, por sua vez, nomeia o elemento exato e qualifica a mudança (deslocado, adicionado, removido, modificado).

Por que o nível do elemento muda tudo

O Delta-QA não compara uma imagem grande. Ele:

  1. reconstrói a árvore dos elementos da página;
  2. associa cada elemento entre as duas versões (pelo seu conteúdo, depois pela sua posição);
  3. só compara os pixels no nível mais fino, e detecta as mudanças próprias de um bloco ignorando as zonas dos seus sub-elementos já modificados;
  4. deixa de lado a suavização das bordas na contagem de pixels realmente diferentes.

Consequência: pode ser muito sensível (detectar uma borda de 1 px num bloco grande) sem se afogar nas variações de suavização, porque esse ruído é deixado de lado e cada sinal está ligado a um elemento preciso. Um deslocamento não é um «bloco vermelho»: é um elemento sinalizado desaparecido na posição antiga e apareceu na nova. A sensibilidade localizada é automática, sem máscaras a preparar com antecedência.

Metodologia — e os seus limites

Levamos o rigor a sério, por isso eis a zona cinzenta:

  • A mesma página para ambos. As duas abordagens partem exatamente da mesma página, renderizada e congelada — sem viés de exibição.
  • Números verificados contra a ferramenta de referência. O nosso banco de testes recalcula a diferença de cor da mesma forma que a ferramenta de comparação pixel a pixel mais usada. Cruzámos os 5 casos com essa ferramenta oficial: na mudança de cor marcada, as duas dão 5,036% contra 5,034% — quase idênticas. Nos outros casos, a ferramenta de referência conta ainda menos pixels (ignora a suavização das bordas) — por isso é ainda mais propensa a deixar passar as pequenas mudanças. Os números da tabela são os dela.
  • O Delta-QA sobre-sinaliza (e assumimos). Na mudança de cor, emite 3 sinais: o verdadeiro (o cabeçalho, intensidade 0,996) + 2 sinais pais muito fracos (0,005 e 0,001). É deliberado: remontamos tudo, e o controle de sensibilidade da interface oculta esses sinais fracos por padrão. Mas sejamos claros: a contagem bruta não é «1 mudança = 1 sinal».
  • Um único contexto de teste. Estas medições são feitas num único tamanho de tela, página em repouso, sobre páginas de teste controladas. Não afirmamos nada sobre vários tamanhos de tela, estados interativos (hover, focus) ou páginas realmente ruidosas — outros projetos.
  • Reordenação. O Delta-QA classificou os cartões reordenados como «modificados» em vez de «deslocados», mas localizados por elemento — o que continua muito acima de um bloco difuso.

E sejamos justos: comparar a imagem inteira é simples, só precisa de uma captura por página, continua excelente para uma quebra grande, e as suas máscaras de zonas funcionam. O problema não é ser mau — é que obriga a escolher entre deixar passar o fino ou gritar pelo ruído, e a configurar a precisão à mão.

O que reter

Se o teu teste de regressão visual assenta numa comparação da imagem inteira com um limiar em porcentagem na configuração padrão, ele provavelmente deixa passar mudanças que os teus utilizadores veem — deslocamentos, cores localizadas, micro-mudanças de estilo. Baixar o limiar detecta-as, mas acorda os falsos alertas; as máscaras ajudam, mas configuram-se zona a zona, com antecedência.

Comparar elemento por elemento não é uma configuração: é outra arquitetura, que devolve ao mesmo tempo a sensibilidade e a precisão — e, como bónus, o nome do elemento e a natureza da mudança.

Para aprofundar


Teste reproduzível: a comparação «imagem inteira» foi produzida com a ferramenta open source de referência (o pacote pixelmatch, Node/npm), na sua sensibilidade padrão, e depois com um limiar de decisão de 0,1% como BackstopJS — sobre exatamente a mesma página congelada que o Delta-QA.