| Bellacosa Mainframe testando software |
☕ Um Café no Bellacosa Mainframe
O Mestre Persa Algoritm Entra no CPD — A Noite em que COBOL Descobriu que “Funcionou Aqui” Não Era Plano de Testes
Ou: como Black Box, White Box, Regression, Selenium, APIs, CI/CD, Db2, CICS, MQ, performance, bugs, usuários furiosos e um velho sábio persa ensinaram a um programador COBOL que qualidade não é provar que o sistema funciona — é descobrir todas as maneiras pelas quais ele pode falhar antes das três da manhã
Há uma velha história, provavelmente inventada por algum operador de mainframe durante um plantão de madrugada, sobre um mestre persa chamado Algoritm.
Alguns dizem que ele veio de Bagdá.
Outros juram que apareceu pela primeira vez em Samarcanda carregando pergaminhos cheios de fórmulas.
Um analista mais desconfiado afirmou que seu verdadeiro nome deveria ser alguma corruptela de Al-Khwarizmi, mas ninguém teve coragem de abrir uma change request para corrigir o cadastro.
O fato é que, numa noite qualquer, o Mestre Persa Algoritm atravessou a porta do CPD.
Olhou para os racks.
Observou as luzes piscando.
Passou pela console do z/OS.
Viu um programador COBOL iniciante comemorando diante da tela.
— Funcionou!
Algoritm parou.
— O que funcionou?
— Meu programa.
— Quantas vezes?
— Uma.
O persa acariciou a barba.
— Então você não sabe se funciona.
— Como assim?
— Você sabe apenas que não falhou daquela maneira específica, naquele instante específico, usando aqueles dados específicos.
O programador ficou em silêncio.
Algoritm puxou uma cadeira.
— Prepare o café. Esta noite falaremos sobre testes.
E é aqui que começa nossa viagem.
1. Testar software não é procurar botão quebrado
Quando alguém está começando em programação, especialmente vindo de uma mentalidade muito orientada ao código, é fácil imaginar teste assim:
executei
↓
não deu abend
↓
está certoNo mainframe existe até uma versão clássica:
JOB ENDED - MAXCC=0000E imediatamente surge a tentação:
“Pronto. Funcionou.”
Não necessariamente.
MAXCC=0000 significa que aquele job terminou sem retornar uma condição de erro significativa naquele processamento.
Não significa:
dados corretos
regras corretas
performance correta
segurança correta
concorrência correta
integração corretaPode existir um sistema absolutamente errado terminando com RC=0.
Essa é uma das primeiras lições do Mestre Algoritm:
Software Testing é uma investigação sobre comportamento e risco.
O teste tenta responder:
O que deveria acontecer?
↓
O que aconteceu?
↓
Existe diferença?
↓
Por quê?
↓
Qual o impacto?Portanto, o objetivo do teste não é somente encontrar bugs.
Ele também serve para:
aumentar confiança no sistema;
reduzir risco;
verificar requisitos;
ajudar decisões de release;
encontrar comportamentos inesperados;
descobrir problemas antes que o cliente descubra.
Porque existe algo pior do que um tester descobrir um bug.
É o cliente descobrir.
2. O primeiro paradoxo: testes nunca provam ausência de bugs
Algoritm pega uma xícara.
— Quantos testes você precisa executar para provar que seu programa não possui erros?
O programador pensa.
— Mil?
— Não.
— Um milhão?
— Não.
— Todos?
Algoritm sorri.
— Agora começou a entender.
Um dos princípios fundamentais dos testes diz:
Testing shows the presence of defects, not their absence.
Ou seja:
se encontramos erro, sabemos que existe erro.
Se não encontramos, apenas não encontramos ainda.
Essa distinção parece filosófica, mas é profundamente prática.
Imagine:
IF WS-SALDO >= WS-SAQUE
SUBTRACT WS-SAQUE FROM WS-SALDO
ELSE
MOVE 'SALDO INSUFICIENTE'
TO WS-MENSAGEM
END-IFTestamos:
saldo = 1000
saque = 100Resultado perfeito.
Mas faltam:
saldo = 100
saque = 100
saldo = 99
saque = 100
saque = 0
saque negativo
saque enorme
saldo inválido
conta bloqueada
duas requisições simultâneasAquele primeiro teste apenas mostrou que:
1000 - 100 = 900Não mostrou que o sistema bancário está pronto para produção.
3. Teste exaustivo é impossível
Agora Algoritm desenha numa folha:
LOGINTemos usuário e senha.
Parece simples.
Mas considere:
usuário correto
usuário errado
senha correta
senha errada
conta bloqueada
conta expirada
senha expirada
campo vazio
espaço
Unicode
caracteres especiais
string gigantesca
tentativas repetidas
sessão existente
rede instável
banco indisponívelE cada uma dessas condições pode combinar-se com outras.
A explosão combinatória acontece rapidamente.
Portanto:
Exhaustive testing is impossible.
É impossível testar cada combinação possível de entrada, estado, ambiente e sequência.
Por isso precisamos de inteligência.
Não testamos tudo.
Testamos aquilo que oferece melhor cobertura de risco.
Esse é o momento em que testes deixam de parecer uma checklist e começam a parecer engenharia.
4. SDLC e STLC — construir e testar não deveriam viver separados
A abordagem tradicional do desenvolvimento frequentemente parecia uma procissão burocrática:
Analista
↓
Desenvolvedor
↓
Tester
↓
ProduçãoO tester recebia o sistema praticamente pronto.
Encontrava 47 problemas.
O desenvolvedor dizia:
“Mas agora vai atrasar o projeto!”
O tester respondia:
“Eu não criei os 47 bugs.”
E começava uma guerra civil.
O SDLC — Software Development Life Cycle cobre o ciclo do software:
requisitos
↓
análise
↓
design
↓
desenvolvimento
↓
testes
↓
deploy
↓
manutençãoJá o STLC — Software Testing Life Cycle trata especificamente das atividades de teste:
análise de requisitos
↓
planejamento
↓
desenho dos testes
↓
preparação do ambiente
↓
execução
↓
encerramentoMas numa organização madura esses ciclos se cruzam.
Testing não deveria começar quando a programação termina.
O tester precisa participar antes.
Isso é o famoso Shift Left.
5. Shift Left — encontrar bugs antes que eles virem código
Algoritm pergunta:
— Qual é o bug mais barato?
O programador responde:
— O que é rápido de corrigir?
— Não. O que ainda não virou código.
Considere um requisito:
“O cliente poderá transferir valores entre contas.”
Parece perfeito.
Até alguém perguntar:
qual limite?
pode enviar para a própria conta?
conta bloqueada pode transferir?
o que ocorre no limite exato?
o que ocorre se o débito acontecer e o crédito falhar?
como evitar transferência duplicada?
há timeout?Essas perguntas podem revelar problemas ainda durante os requisitos.
Nenhuma linha COBOL foi compilada.
Nenhum JCL foi submetido.
Nenhum DBA foi acordado.
Esse é um dos maiores ganhos de testes modernos:
testar ideias antes de testar programas.
6. Os níveis de teste — do parafuso ao avião inteiro
O material apresenta níveis clássicos:
Unit
↓
Integration
↓
System
↓
AcceptancePodemos pensar numa fábrica de aviões.
Você testa:
parafuso
motor
asa
avião completo
vooNão basta verificar o parafuso.
Também não faz sentido esperar o avião completo para descobrir que o parafuso estava errado.
7. Unit Testing — teste perto do código
Unit Test testa uma pequena unidade isoladamente.
Imagine:
COMPUTE WS-JUROS =
WS-SALDO * WS-TAXAQueremos testar:
saldo positivo
saldo zero
saldo negativo
taxa zero
decimais
arredondamento
overflowUnit Tests são normalmente:
rápidos
repetíveis
isolados
baratosEles ajudam a encontrar o problema perto de onde ele nasceu.
No ecossistema COBOL moderno há inclusive ferramentas e frameworks voltados a unit testing, e isso destrói a velha lenda:
“COBOL não combina com testes modernos.”
Combina perfeitamente.
O código não sabe que nasceu em 1959.
8. Integration Testing — o sistema quebra quando começa a conversar
Aqui temos um fenômeno clássico.
Todos os módulos funcionam.
Juntos, não.
Imagine:
Aplicativo
↓
API Gateway
↓
z/OS Connect
↓
CICS
↓
COBOL
↓
Db2Separadamente:
API OK
CICS OK
COBOL OK
Db2 OKMas integração pode falhar porque:
JSON ≠ copybook
UTF-8 ≠ EBCDIC
decimal ≠ COMP-3
timestamp ≠ formato esperado
campo obrigatório desapareceuIntegration Testing verifica os contratos entre os componentes.
É aí que aparecem erros que ninguém tinha quando estava sozinho.
Quase como reunião de família.
9. System Testing — teste aquilo que o usuário realmente percebe
O usuário não pensa:
“Estou invocando um microsserviço de estoque.”
Ele pensa:
“Estou comprando um notebook.”
Logo, System Testing examina o fluxo completo.
login
↓
busca
↓
produto
↓
carrinho
↓
checkout
↓
pagamento
↓
estoque
↓
pedido
↓
confirmaçãoPode haver vinte componentes internos.
Para o cliente existe apenas uma pergunta:
“Minha compra funcionou?”
Essa diferença é essencial.
10. Functional Testing versus Non-Functional Testing
Aqui está uma das divisões mais elegantes.
Functional Testing pergunta:
O que o sistema faz?
Non-Functional Testing pergunta:
Quão bem ele faz?
Imagine transferência bancária.
Funcionalmente:
debita origem
credita destino
gera comprovantePerfeito.
Mas leva 45 segundos.
Tecnicamente funciona.
Praticamente é um desastre.
Ou funciona rapidamente, mas:
qualquer cliente consulta a conta de outro clienteFuncionalmente responde.
Em segurança, falhou monstruosamente.
Por isso software de qualidade precisa dos dois lados.
11. Black Box Testing — teste o sistema sem olhar dentro
Black Box é como testar uma máquina fechada.
Você conhece:
entrada
resultado esperadoNão precisa saber como o programa foi implementado.
Exemplo:
idade aceita: 18 até 65Não testamos todos os números do universo.
Usamos técnicas.
12. Equivalence Partitioning — dividir o universo
Podemos criar partições:
idade < 18 inválida
18–65 válida
idade > 65 inválidaSelecionamos representantes:
17
30
66Isso reduz bastante o número de testes mantendo boa cobertura.
Mestre Algoritm comenta:
— Um bom tester não testa muito. Testa inteligentemente.
13. Boundary Value Analysis — o perigo mora na fronteira
Programadores são criaturas especialmente vulneráveis aos limites.
Se a regra é:
1 até 100testes interessantes:
0
1
2
99
100
101Porque bugs surgem em diferenças como:
<
<=
>
>=Em COBOL pense imediatamente em:
PIC
OCCURS
subscripts
COMP-3
tamanho de registro
campo numéricoA fronteira é um habitat natural de bugs.
14. Decision Table — excelente para regra de negócio
Imagine autorização de crédito:
cliente ativo?
saldo suficiente?
limite disponível?
conta regular?Cada condição possui combinações.
Uma Decision Table deixa explícito:
| Ativo | Saldo | Limite | Resultado |
|---|---|---|---|
| Sim | Sim | Sim | Aprova |
| Não | Sim | Sim | Rejeita |
| Sim | Não | Sim | Rejeita |
| Sim | Sim | Não | Rejeita |
É particularmente valiosa em sistemas financeiros cheios de regras condicionais.
E sistemas COBOL adoram regras condicionais.
Às vezes até demais.
15. State Transition Testing — o passado importa
Alguns comportamentos dependem do estado.
Considere login:
ACTIVE
↓ erro
ACTIVE
↓ erro
ACTIVE
↓ erro
LOCKEDO terceiro erro produz algo diferente do primeiro.
Portanto:
estado atual
+
evento
=
novo estadoIsso aparece em:
login
pagamentos
pedidos
CICS
workflow
mensageriaTesting de estado verifica justamente essas transições.
16. White Box Testing — agora abrimos o programa
No White Box conhecemos a estrutura interna.
Imagine:
IF A > B
PERFORM ROTINA-A
ELSE
PERFORM ROTINA-B
END-IFPrecisamos exercitar:
True
FalseE podemos medir:
Statement Coverage
Branch Coverage
Condition Coverage
Path Coverage
Loop CoverageMas atenção ao Easter Egg escondido no templo persa:
100% coverage não significa 100% correto.
Podemos executar todas as linhas de um programa errado.
Coverage mede execução.
Não mede sabedoria.
17. Regression versus Retesting — duas perguntas diferentes
Bug:
senha contendo @ falhaCorrigimos.
Retesting
Pergunta:
A correção funcionou?
Testamos novamente a senha.
Regression
Pergunta:
A correção quebrou outra coisa?
Testamos:
login
logout
reset password
MFA
sessão
bloqueioResumo Bellacosa:
RETEST:
"Consertou o vazamento?"
REGRESSION:
"Consertando o vazamento, estourou o encanamento da cozinha?"Essa é a diferença.
18. Defect Life Cycle — o bug também tem carreira
Um bug costuma viajar:
New
↓
Assigned
↓
Open
↓
In Progress
↓
Fixed
↓
Retest
↓
Verified
↓
ClosedMas no mundo real encontramos parentes:
Reopened
Rejected
Duplicate
Deferred
Cannot Reproduce
Won't Fix
BlockedO mais famoso é:
Cannot Reproduce.
O tester jura que aconteceu.
O developer jura que não.
Produção entra na reunião e diz:
Aconteceu comigo também.
Silêncio.
Um bom bug report precisa conter:
versão
ambiente
pré-condições
passos
dados
resultado esperado
resultado observado
timestamp
logs
evidências“Não funciona” não é bug report.
É pedido de ajuda.
19. Severity e Priority — gravidade não é urgência
Severity:
Quanto dói?
Priority:
Quão rápido precisamos tratar?
Imagine erro ortográfico na tela principal durante uma demonstração internacional.
Severity:
LowPriority:
P1Agora imagine crash crítico numa função administrativa usada uma vez ao ano.
Severity:
CriticalPriority pode não ser P1.
Portanto:
SEVERITY != PRIORITYSeverity está relacionada ao impacto do defeito.
Priority envolve necessidade de negócio.
20. Manual Testing — humanos ainda são surpreendentemente úteis
Chegamos a uma moda perigosa:
“IA e automação vão eliminar teste manual.”
Não.
Teste manual é particularmente útil em:
exploratory testing
usability
interface
novas features
comportamentos inesperados
investigaçãoO ser humano percebe coisas que um script não foi instruído a perceber.
Um script pode validar:
botão existeUm humano percebe:
“Ninguém vai entender para que serve esse botão.”
São problemas diferentes.
21. Test Automation — automatize repetição, não pensamento
Automação faz sentido quando testes são:
repetitivos
estáveis
frequentes
determinísticos
caros manualmenteRegression Testing é candidato clássico.
Mas automatizar tudo gera outra doença:
10.000 scripts
+
interface alterada
=
10.000 problemas de manutençãoAutomação tem ROI.
Existe:
custo inicial
infraestrutura
manutenção
dados
execução
investigaçãoPor isso:
Automatize o teste certo, não simplesmente tudo que possui botão.
22. A pirâmide dos testes
A pirâmide clássica:
UI
/--\
API
/----\
UNIT
/______\A base contém muitos Unit Tests.
Depois APIs e integração.
No topo poucos E2E/UI.
Por quê?
Unit Tests são rápidos.
UI Tests são relativamente lentos e frágeis.
Se toda regra de negócio for validada por Selenium abrindo browser, clicando em menus e esperando telas, prepare café.
Muito café.
23. Selenium — o mensageiro do navegador
Selenium automatiza navegadores.
Fluxo:
test script
↓
WebDriver
↓
browser
↓
applicationPodemos:
abrir URL
localizar elementos
clicar
preencher
capturar texto
validar resultadoO Selenium é poderoso.
Mas não deve testar tudo.
Se uma regra pode ser verificada diretamente na API, normalmente o teste será:
mais rápido
mais estável
mais simples24. O clássico sleep(5) — a oração do programador cansado
Considere:
Thread.sleep(5000);Tradução:
“Ó sistema, por favor tenha terminado dentro de cinco segundos.”
Se termina em 100 ms:
perdemos tempo.
Se termina em 5,01 segundos:
falha.
Melhor é esperar uma condição:
botão clicável
elemento visível
resposta concluídaEsses são os Explicit Waits.
Eles ajudam a reduzir testes flakey.
Flaky Test é aquele teste adorável que:
passa
falha
passa
falhasem mudança no programa.
O Mestre Algoritm certamente jogaria esse teste no deserto.
25. API Testing — HTTP 200 não é certificado de perfeição
API Testing verifica:
endpoint
method
headers
authentication
request
response
schema
status
performance
securityImagine:
POST /transferRecebemos:
HTTP 200Excelente?
Talvez.
Precisamos verificar:
o dinheiro saiu?
chegou?
duplicou?
foi persistido?
auditado?Uma resposta HTTP tecnicamente válida pode carregar comportamento de negócio errado.
Esse é um conceito extremamente importante.
26. Performance Testing — o sistema pode funcionar e ainda assim morrer
Performance Testing não significa apenas medir velocidade.
Temos:
Load Testing
Carga esperada.
Stress Testing
Carga além do esperado.
Spike Testing
Aumento repentino.
Soak Testing
Carga prolongada.
Volume Testing
Grandes volumes de dados.
Scalability Testing
Capacidade de crescer.
Uma aplicação pode sobreviver cinco minutos perfeitamente.
Depois de seis horas:
memory leak
pool esgotado
fila crescente
storage acabandoO Soak Test revela esses monstros.
27. Métricas — média é uma mentirosa educada
Imagine:
Average Response Time = 200 msFantástico.
Mas:
P50 = 100 ms
P95 = 2 s
P99 = 12 sA maioria está feliz.
Uma parcela está sofrendo profundamente.
Por isso usamos percentis.
Também observamos:
TPS
throughput
latency
error rate
CPU
memory
disk
networkUma métrica isolada conta metade da história.
28. Agile Testing — testing deixa de ser departamento
Agile Testing traz uma mudança fundamental:
qualidade é responsabilidade do time.
Não:
DEV fez
↓
QA testaMas:
PO
↕
Developer
↕
Tester
↕
OperationsTesting acontece continuamente.
O QA deixa de ser “polícia do bug”.
Torna-se parceiro da engenharia.
29. CI/CD — a esteira que não aceita promessa
No CI/CD:
commit
↓
build
↓
unit test
↓
static analysis
↓
integration
↓
API test
↓
security
↓
deploy
↓
monitorSe um quality gate falha:
STOPNo mainframe isso pode envolver:
Git
↓
DBB
↓
COBOL Compile
↓
Link Edit
↓
Unit Tests
↓
Integration
↓
DeploySim.
COBOL pode viver dentro de pipeline moderno.
Ele não precisa continuar esperando fita magnética e formulário de mudança datilografado.
30. “Works in DEV, fails in PROD”
Existe uma inscrição secreta na parede de todo datacenter:
Funciona no meu ambiente.
Em produção encontramos diferenças:
configuração
dados
versões
RACF
certificados
network
Db2
CICS
feature flags
capacityCódigo igual não garante ambiente igual.
Portanto:
ambiente também faz parte do sistema.
Essa ideia é frequentemente esquecida.
31. O cenário assustador: usuário vendo dados de outro usuário
Imagine:
Cliente A
↓
consulta conta
↓
dados do Cliente BO sistema pode responder:
HTTP 200
tempo = 70 ms
JSON válidoPerformance maravilhosa.
Funcionalidade respondendo.
Segurança devastada.
Aqui entram:
authentication
authorization
session
access control
data isolationEssa é a prova definitiva de que qualidade é multidimensional.
32. “Dados às vezes não são salvos”
Algoritm fica sério.
— Qual palavra mais perigosa num bug report?
O programador responde:
— Abend?
— Não.
— Corrupção?
— Não.
— Qual?
— Às vezes.
“Às vezes não salva.”
Pode indicar:
race condition
lock
timeout
rollback
commit
deadlock
concorrência
network failureProblemas intermitentes são particularmente difíceis porque dependem de timing e estado.
Exemplo:
DEBIT
↓
serviço externo
↓
timeout
↓
CREDIT?Esse tipo de teste começa a entrar em resiliência.
33. Observabilidade — a página que deveria existir
Eu acrescentaria ao material:
Testing + Observability
Não basta saber:
FAILEDPrecisamos saber:
WHY?Logs.
Metrics.
Traces.
No mainframe:
SQLCODE
CICS RESP
MQ Reason Code
SMF
return codes
Abend codesEm sistemas distribuídos, um correlation ID permite acompanhar:
Mobile
↓
API
↓
z/OS Connect
↓
CICS
↓
COBOL
↓
Db2Sem observabilidade, debugging moderno vira arqueologia.
34. Resilience Testing — provoque o desastre antes que o desastre aconteça
Outra evolução interessante:
E se Db2 cair?
E se MQ parar?
E se a rede ficar lenta?
E se o serviço externo devolver lixo?
E se o certificado expirar?Resilience Testing não pergunta apenas:
o sistema quebra?
Pergunta:
como o sistema quebra?
Existe enorme diferença entre:
mensagem de indisponibilidadee:
dados financeiros inconsistentesSistemas críticos precisam falhar com segurança.
35. E finalmente: como pensa um tester de verdade?
Voltamos ao Mestre Algoritm.
O iniciante pergunta:
— Mestre, então qual é a principal ferramenta de um tester?
Algoritm aponta para a cabeça.
Não Selenium.
Não Postman.
Não JMeter.
Não Jenkins.
Não alguma ferramenta de IA.
A principal ferramenta é:
curiosidade estruturada.
O desenvolvedor pergunta:
Como faço isso funcionar?O tester pergunta:
Como faço isso falhar?O engenheiro de qualidade pergunta:
Qual falha importa mais?O engenheiro de resiliência pergunta:
Quando isso inevitavelmente falhar,
como impedimos que o desastre se espalhe?Isso representa uma progressão extremamente interessante:
JÚNIOR
"Funciona?"
↓
PLENO
"Quando falha?"
↓
SÊNIOR
"Onde pode falhar?"
↓
TEST ENGINEER
"Como mensuramos o risco?"
↓
QUALITY ENGINEER
"Como prevenimos e detectamos?"
↓
SRE / RESILIENCE
"Como sobrevivemos à falha?"☕ Epílogo — o Mestre Persa desliga o terminal
O relógio marcava 03:17.
O café havia acabado.
O programador COBOL olhou novamente para seu job.
Na tela continuava:
MAXCC=0000Antes ele teria comemorado.
Agora começou a perguntar:
E se o arquivo estiver vazio?
E se chegar duplicado?
E se o Db2 retornar -911?
E se o CICS der timeout?
E se dois usuários fizerem isso juntos?
E se MQ não responder?
E se a regra mudar?
E se o sistema estiver lento?
E se um usuário acessar dados de outro?
E se funcionar hoje e quebrar amanhã?Algoritm sorriu.
— Agora você está testando.
Pegou seu velho pergaminho.
Saiu pelo corredor.
No verso havia uma última frase escrita em tinta desbotada:
“O bom programador escreve caminhos pelos quais o software pode funcionar. O bom tester procura caminhos pelos quais ele pode falhar. O grande engenheiro entende que ambos estão desenhando o mesmo sistema.”
Na manhã seguinte ninguém encontrou o Mestre Algoritm.
Mas sobre a mesa do programador havia uma pequena folha contendo apenas:
IDENTIFY
RISK
DESIGN
TEST
EXECUTE
EVIDENCE
LEARN
REPEATE, escondido no canto inferior, quase como um Easter Egg:
IF PROGRAM-WORKS
CONTINUE
ELSE
CALL 'MESTRE-ALGORITM'
END-IF.O compilador reclamou que MESTRE-ALGORITM não existia.
Naturalmente.
O primeiro teste da manhã havia acabado de encontrar um bug.