| Bellacosa Mainframe e os testes automatizados |
☕ Um Café no Bellacosa Mainframe
Dr. Strangelove e a Arte de Parar de se Preocupar e Amar os Testes
🧪 Um guia para o programador COBOL iniciante entender Unit Test, Component Test, Integration, E2E, APIs, Db2, CICS, IA, Mutation Testing, Chaos Engineering e por que um MAXCC=0 não significa que o mundo está salvo
Imagine a cena.
Uma sala subterrânea.
Luzes fracas.
Painéis verdes piscando.
Um enorme mapa operacional ocupa a parede.
Generais discutem nervosamente ao redor de uma mesa enquanto alguém observa um terminal 3270.
De repente, surge a mensagem:
IEF142I PAYROLL STEP030 - STEP WAS EXECUTED - COND CODE 0000
Silêncio.
Um jovem programador COBOL ergue os braços:
— Funcionou!
Do outro lado da mesa, um velho sysprog ajusta os óculos.
— O job terminou com RC=0.
— Exatamente! Funcionou!
— Eu disse que terminou.
— E qual é a diferença?
Nesse momento, uma música imaginária começa a tocar e, em algum lugar entre o JES2, o Db2 e um endpoint REST perdido na nuvem, o Dr. Strangelove levanta lentamente a mão mecânica.
Bem-vindo ao maravilhoso mundo dos testes de software.
Porque existe uma diferença monumental entre:
O PROGRAMA EXECUTOU
e:
O PROGRAMA FEZ A COISA CERTA.
E existe uma diferença ainda maior entre:
O PROGRAMA FEZ A COISA CERTA UMA VEZ.
e:
O SISTEMA CONTINUARÁ FAZENDO A COISA CERTA
QUANDO O BANCO ESTIVER LENTO,
O MQ ESTIVER INDISPONÍVEL,
O USUÁRIO CLICAR DUAS VEZES,
O RACF NEGAR ACESSO
E UM ARQUIVO CHEGAR COM 17 BYTES A MENOS.
Essa diferença se chama engenharia.
🦖 Antes de qualquer coisa: o teste não existe para provar que você é inteligente
Essa talvez seja a primeira lição para quem está começando em COBOL.
Testar não é demonstrar que o programa está bonito.
Não é mostrar ao chefe:
100% DE COVERAGE
e sair correndo em direção ao café.
Testar é produzir evidência de que o comportamento esperado existe.
Pense em um programa COBOL simples:
IF WS-IDADE >= 18
MOVE 'MAIOR' TO WS-STATUS
ELSE
MOVE 'MENOR' TO WS-STATUS
END-IF.
Parece fácil.
Então alguém testa:
IDADE = 25
RESULTADO = MAIOR
Passou.
Parabéns.
Temos uma evidência.
Mas ainda sabemos pouco.
Teste também:
IDADE = 17
IDADE = 18
IDADE = 19
IDADE = 0
IDADE = 999
IDADE = -1
Nesse ponto começamos a perceber algo fundamental.
O trabalho interessante não é apenas perguntar:
funciona?
A pergunta interessante é:
em quais condições funciona, em quais condições falha e o que significa falhar corretamente?
🔬 UNIT TEST — o microscópio do programador
Unit Test é o teste da menor unidade lógica razoável.
Em COBOL isso pode ser:
um parágrafo;
uma SECTION;
uma rotina;
uma regra de negócio isolada;
uma transformação;
um cálculo.
Imagine:
CALCULA-JUROS.
COMPUTE WS-JUROS =
WS-VALOR * WS-TAXA / 100.
Podemos testar:
VALOR = 1000
TAXA = 10
JUROS = 100
Mas um bom programador vai além:
VALOR = 0
TAXA = 10
VALOR = 1000
TAXA = 0
VALOR = 1000
TAXA = -10
VALOR = 999999999
TAXA = 99.99
Aqui aparece uma das primeiras curiosidades importantes para quem entra no mundo COBOL:
💰 Dinheiro é uma criatura traiçoeira
COBOL é historicamente excelente para sistemas financeiros justamente porque possui representação decimal adequada.
Mesmo assim, você precisa pensar em:
PIC 9(9)V99
PIC S9(9)V99 COMP-3
ROUNDED
ON SIZE ERROR
Um cálculo pode estar matematicamente correto e ainda produzir problema de tamanho, arredondamento ou sinal.
O Unit Test serve justamente para transformar essas hipóteses em fatos verificáveis.
🧩 COMPONENT TEST — quando várias engrenagens começam a conversar
Um componente é maior que uma função isolada.
Imagine um programa:
PROGRAMA DE CADASTRO
VALIDA CPF
↓
VALIDA DATA
↓
CALCULA SCORE
↓
CLASSIFICA CLIENTE
↓
MONTA REGISTRO
Você pode testar cada rotina separadamente.
Depois pode testar o componente completo sem necessariamente acessar Db2, CICS ou MQ.
Por exemplo:
ENTRADA
CPF válido
idade 43
renda 8000
SAÍDA
cliente aceito
score 742
categoria B
Isso começa a testar o comportamento da aplicação como conjunto.
É um pouco como testar o motor inteiro depois de testar pistão, válvula e injeção separadamente.
🟩 INTEGRATION TEST — “Vocês dois realmente conseguem conversar?”
Aqui começam algumas das melhores explosões cinematográficas.
Imagine:
PROGRAMA COBOL
↓
Db2
Seu programa pode estar perfeito.
Sua tabela também.
Mas a integração pode falhar porque:
coluna mudou
tipo mudou
package não está bindado
plan errado
authorization falhou
SQLCODE inesperado
connection indisponível
Ou:
COBOL
↓
MQ
↓
JAVA
O programa COBOL envia:
CUSTOMER-ID=00001234
O Java espera:
customerId=1234
Os dois programas podem passar individualmente em todos os testes.
Juntos:
💥
Esse é o território do Integration Testing.
🐳 Testcontainers e a revolução do “vamos testar com algo parecido com produção”
No mundo distribuído moderno existe uma ferramenta muito interessante chamada Testcontainers.
Ela permite criar dependências reais durante o teste.
Por exemplo:
TESTE
↓
sobe PostgreSQL
↓
executa aplicação
↓
testa
↓
derruba PostgreSQL
Isso evita uma velha armadilha:
Produção = banco A
Teste = banco B parecido
A palavra perigosa aqui é:
parecido.
Em computação, “parecido” é frequentemente irmão de “incidente”.
No mainframe temos uma filosofia semelhante quando criamos ambientes de teste com Db2, VSAM, MQ, CICS e configurações próximas de produção.
Quanto maior a fidelidade do ambiente, maior tende a ser nossa confiança.
Mas também maior o custo.
E aqui começa a aparecer a famosa pirâmide.
🔺 A PIRÂMIDE DOS TESTES
Imagine:
/\
/ \
/ E2E\
/------\
/INTEGR. \
/----------\
/ COMPONENT \
/--------------\
/ UNIT \
/___________________\
A ideia é simples.
Na base:
MUITOS TESTES
RÁPIDOS
BARATOS
ISOLADOS
No topo:
POUCOS TESTES
LENTOS
CAROS
COM MUITAS DEPENDÊNCIAS
Não existe uma porcentagem mágica.
Você encontrará gente repetindo:
70% Unit
20% Integration
10% E2E
Não trate isso como mandamento gravado em pedra por Moisés depois de um IPL.
É apenas uma heurística.
A arquitetura do sistema deve determinar a estratégia.
🎭 END-TO-END — agora queremos saber se o usuário consegue sobreviver
End-to-End Test, ou E2E, acompanha uma jornada completa.
Imagine Internet Banking:
cliente abre navegador
↓
login
↓
consulta conta
↓
faz transferência
↓
confirma
↓
recebe comprovante
Nos bastidores:
Browser
↓
Frontend
↓
API Gateway
↓
OAuth
↓
z/OS Connect
↓
CICS
↓
COBOL
↓
Db2
↓
MQ
↓
outro sistema
Agora temos algo muito próximo da realidade.
Excelente.
Só existe um pequeno problema:
se o teste falhar, quem matou o coronel?
Pode ser:
browser
CSS selector
rede
token
API
DNS
z/OS Connect
CICS
COBOL
Db2
MQ
serviço remoto
ambiente de teste
Um único:
FAILED
pode significar vinte coisas diferentes.
É por isso que E2E é extremamente valioso, mas também caro.
👻 O monstro chamado Flaky Test
Agora chegamos a uma criatura verdadeiramente perigosa.
O flaky test.
Hoje:
PASS
Amanhã:
FAIL
Executa novamente:
PASS
O programador aprende:
quando falhar, roda novamente
Essa é uma péssima situação.
Depois de alguns meses:
PIPELINE FAILED
e alguém responde:
— Deve ser aquele teste.
Roda novamente.
Passa.
A partir desse momento, o sistema de testes começou a perder autoridade.
Isso é equivalente a um alarme de incêndio que dispara todos os dias sem incêndio.
No começo todo mundo corre.
Depois de três meses:
— Ah, é aquele alarme.
Até o dia em que o prédio realmente pega fogo.
📊 COVERAGE — a estatística favorita para impressionar PowerPoint
Imagine:
IF WS-SALDO >= WS-VALOR
PERFORM EFETUA-SAQUE
END-IF.
Você cria um teste:
SALDO = 1000
VALOR = 100
A linha foi executada.
Coverage:
100%
Maravilhoso.
Mas você testou:
SALDO = 100
VALOR = 100
?
E:
SALDO = 99
VALOR = 100
?
Coverage mede frequentemente:
o código foi executado?
Não necessariamente:
o comportamento foi corretamente verificado?
É uma diferença monumental.
🧬 MUTATION TESTING — quem testa os testes?
Essa técnica é maravilhosa.
Imagine seu código:
IF WS-IDADE >= 18
Uma ferramenta de mutation testing altera deliberadamente para:
IF WS-IDADE > 18
Depois executa seus testes.
Se eles continuarem passando:
🤨
talvez ninguém esteja realmente testando a fronteira:
IDADE = 18
Mutation Testing basicamente pergunta:
se eu introduzir um pequeno defeito proposital, seus testes percebem?
É uma pergunta muito mais poderosa do que apenas coverage.
🔐 SECURITY TESTING — “funcionar” não significa “ser seguro”
Imagine:
Administrador consegue consultar salários?
Teste:
PASS
Ótimo.
Agora faça a pergunta de segurança:
Usuário comum consegue consultar salários?
Se a resposta for sim:
Parabéns.
Sua aplicação funciona perfeitamente
e está perfeitamente vulnerável.
Security Testing precisa verificar:
autenticação
autorização
privilégio
validação de entrada
segregação
sessão
tokens
exposição de dados
No mainframe:
RACF
SAF
ACEE
profiles
FACILITY
SURROGAT
dataset access
CICS transactions
Um programa COBOL pode produzir exatamente o resultado esperado e ainda ser um desastre de segurança.
⚡ PERFORMANCE TESTING — funciona com um usuário. E com cinquenta mil?
Imagine:
transação demora 50 ms
Excelente.
Então chegam:
10 usuários
100 usuários
1.000 usuários
20.000 usuários
Aí começamos a medir:
CPU
elapsed time
throughput
locks
threads
connections
I/O
queue depth
Db2 contention
CICS MXT
WLM
E essa é uma das grandes vantagens pedagógicas de estudar testes pensando em mainframe.
O mainframe sempre viveu no mundo onde:
funcionalidade e capacidade são problemas diferentes.
🐒 CHAOS ENGINEERING — Dr. Strangelove finalmente assume o comando
Chaos Engineering parece uma ideia de alguém que bebeu café demais:
vamos quebrar deliberadamente coisas para ver o que acontece.
Exemplo:
derrube um serviço
ou:
adicione latência
ou:
bloqueie acesso ao banco
ou:
mate uma instância
e observe.
A pergunta não é:
o sistema funciona?
A pergunta é:
o sistema continua se comportando
de maneira aceitável quando algo falha?
No mundo COBOL podemos traduzir a ideia para:
Db2 indisponível
MQ indisponível
arquivo faltando
arquivo cheio
dataset em uso
VSAM file status diferente de 00
transaction timeout
deadlock
CICS region indisponível
Sistemas empresariais não são construídos apenas para o mundo feliz.
Eles precisam sobreviver ao mundo real.
🦕 TESTANDO BATCH — o capítulo que os diagramas modernos quase sempre esquecem
Agora chegamos à terra natal do COBOL.
Imagine:
JOB001
↓
gera arquivo
↓
JOB002
↓
atualiza Db2
↓
JOB003
↓
manda MQ
↓
JOB004
↓
gera relatório
Você precisa testar:
JCL
PROC
COND
IF/THEN/ELSE
GDG
DISP
datasets
SORT
IDCAMS
restart
checkpoint
commit
rerun
Exemplo:
STEP010 RC=0
STEP020 RC=4
O STEP030 deveria rodar?
Depende.
E se:
STEP020 RC=8
?
E se o arquivo estiver vazio?
E se houver registro duplicado?
E se o job cair depois de atualizar metade da tabela?
Você consegue reiniciar?
O processamento é idempotente?
Esse último termo merece atenção.
🔁 Idempotência
Significa, simplificando:
executar novamente não deve causar destruição adicional.
Imagine pagamento:
PAGA R$100
Job cai.
Operador reinicia.
Se o programa pagar novamente:
R$200 enviados
temos um pequeno incidente.
Se repetirmos 300 mil registros...
temos reunião com vice-presidente.
🤖 AGORA A IA ENTRA NA SALA DE GUERRA
Chegamos a GitHub Copilot, ChatGPT, Claude, Cursor, Qodo e outros.
Essas ferramentas conseguem ajudar a:
gerar testes
encontrar edge cases
criar mocks
produzir dados
documentar
explicar erros
analisar cobertura
sugerir cenários
Você pode entregar:
IF WS-SALDO >= WS-SAQUE
SUBTRACT WS-SAQUE FROM WS-SALDO
END-IF
e perguntar:
Quais casos devo testar?
A IA provavelmente sugerirá:
saldo maior
saldo igual
saldo menor
saque zero
saque negativo
saldo zero
valor máximo
Isso economiza muito tempo.
Mas existe uma pegadinha digna de Stanley Kubrick.
☢️ IA gera o código. IA gera o teste. Tudo passa.
Imagine:
IA interpreta requisito errado
↓
gera implementação errada
↓
IA usa a mesma interpretação
↓
gera teste errado
↓
PASS
Temos então uma maravilha tecnológica:
um erro perfeitamente documentado, testado, automatizado e aprovado pelo pipeline.
Essa talvez seja uma das grandes questões da engenharia assistida por IA.
Quanto mais fácil fica gerar código, mais importante fica validar intenção.
🧠 O programador do futuro precisará responder menos “como?” e mais “o quê?”
Antes:
Como escrevo este teste?
Agora uma IA pode escrever boa parte dele.
A pergunta mais importante passa a ser:
O QUE precisa ser testado?
E principalmente:
O QUE ninguém lembrou de testar?
A experiência começa exatamente aí.
Um programador iniciante testa:
usuário fez tudo certo
Um programador experiente pergunta:
usuário clicou duas vezes?
Depois:
a rede caiu no meio?
Depois:
a mensagem chegou duplicada?
Depois:
o token expirou?
Depois:
Db2 deu deadlock?
Depois:
o job reiniciou após commit parcial?
A maturidade em testes não consiste em escrever mais ASSERT.
Consiste em imaginar mais maneiras pelas quais a realidade pode destruir nossas suposições.
🛠️ PASSO A PASSO PARA O COBOLISTA INICIANTE
Vamos transformar tudo em um roteiro prático.
Passo 1 — descubra a regra
Antes de testar:
ENTRADA
PROCESSAMENTO
SAÍDA
Exemplo:
SALDO = 1000
SAQUE = 300
SALDO FINAL = 700
Passo 2 — teste o Happy Path
Comece pelo comportamento esperado.
saldo suficiente
valor válido
cliente válido
Passo 3 — teste fronteiras
Se a regra diz:
IDADE >= 18
teste:
17
18
19
Essa pequena técnica encontra uma quantidade absurda de erros.
Passo 4 — teste entradas ruins
zero
negativo
nulo
branco
máximo
mínimo
duplicado
formato inválido
Passo 5 — teste integrações
Pergunte:
e se Db2 falhar?
e se VSAM retornar file status 23?
e se MQ estiver indisponível?
e se CICS devolver erro?
Passo 6 — teste restart
Principalmente batch.
Pergunte:
se cair no meio,
consigo continuar?
Passo 7 — teste segurança
Pergunte:
quem pode executar?
quem pode ver?
quem pode alterar?
Passo 8 — teste carga
Pergunte:
funciona com uma transação?
Depois:
e com 10 mil?
Passo 9 — observe produção
Esse ponto é frequentemente esquecido.
Produção ensina.
Logs, abends, SMF, RMF, CICS statistics, Db2 traces e incidentes mostram situações que talvez ninguém tenha imaginado em laboratório.
Cada incidente deveria produzir, quando possível:
novo conhecimento
↓
novo teste
🎁 EASTER EGGS PARA QUEM CHEGOU ATÉ AQUI
Primeiro:
Dr. Strangelove é uma referência óbvia ao filme de Stanley Kubrick:
Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb.
Nossa versão poderia ser:
How I Learned to Stop Worrying and Love the Test Suite.
Segundo:
Se você encontrar um programador COBOL dizendo:
RC=0, então está certo.
não discuta.
Ofereça café.
Depois pergunte delicadamente:
“Mas você conferiu o arquivo de saída?”
Terceiro:
O verdadeiro teste de integração de qualquer sistema corporativo acontece sexta-feira às 17h43 quando alguém diz:
“É só uma alteração pequena.”
Quarto:
Todo programador algum dia conhece este comando psicológico:
RERUN
Passou na segunda vez?
Ótimo.
Agora você ganhou um problema muito pior.
☕ REGRA BELLACOSA Nº 1
Nunca confunda:
JOB TERMINOU
com:
PROCESSAMENTO CORRETO
Nunca confunda:
TESTE PASSOU
com:
SISTEMA ESTÁ CORRETO
Nunca confunda:
100% COVERAGE
com:
100% CONFIANÇA
E nunca confunda:
IA GEROU 47 TESTES
com:
ALGUÉM ENTENDEU O RISCO.
🧨 A grande conclusão: Testing não é sobre código
No fundo, Testing é engenharia de incerteza.
Estamos tentando responder:
Quanto sabemos sobre o comportamento deste sistema?
Depois:
O que ainda não sabemos?
E finalmente:
Quanto custa descobrir isso somente em produção?
Essa última pergunta muda tudo.
Um bug descoberto durante Unit Test talvez custe minutos.
Durante Integration Test, horas.
Durante homologação, dias.
Depois da implantação, talvez semanas.
Depois de afetar 500 mil clientes...
PowerPoint.
War Room.
Conference Call.
RCA.
Executivos.
Compliance.
Auditoria.
E alguém inevitavelmente perguntará:
Como nossos testes não pegaram isso?
A pergunta quase nunca possui resposta simples.
Porque sistemas reais não são apenas código.
São:
código
+
dados
+
infraestrutura
+
rede
+
configuração
+
segurança
+
operações
+
processos
+
humanos
É por isso que nenhum teste isolado consegue provar que o sistema inteiro é perfeito.
A Pirâmide de Testes, o Testing Trophy, Mutation Testing, Security Testing, Performance Testing e Chaos Engineering são maneiras diferentes de iluminar regiões diferentes da escuridão.
E isso nos leva à talvez mais importante transformação trazida pela inteligência artificial.
Durante décadas, programadores foram valorizados em grande parte por sua capacidade de produzir código.
Agora produzir código está ficando barato.
Muito barato.
Uma IA consegue produzir em segundos aquilo que um desenvolvedor levaria horas para digitar.
Mas produzir confiança continua caro.
Porque alguém precisa entender:
qual é a regra?
qual é o risco?
qual é a exceção?
qual é a fronteira?
qual comportamento seria perigoso?
qual falha é aceitável?
qual falha é catastrófica?
E isso não é simplesmente programação.
Isso é julgamento.
Talvez o programador COBOL de 2030 escreva muito menos código manualmente.
Talvez peça:
“Gere um programa COBOL para esta regra.”
A IA produzirá.
Depois:
“Gere os testes.”
A IA produzirá.
Mas alguém ainda precisará olhar para tudo aquilo e fazer a pergunta que nenhum compilador consegue responder sozinho:
Estamos testando aquilo que realmente importa?
E, quando você chegar a esse ponto, terá deixado de ser apenas alguém que escreve COBOL.
Terá começado a pensar como engenheiro de sistemas.
Em algum lugar distante, o JES2 imprimirá:
MAXCC=0000
Você olhará para a tela.
Não comemorará imediatamente.
Tomará um gole de café.
Abrirá o output.
Conferirá os registros.
Verificará os totais.
Examinará o SQLCODE.
Confirmará os datasets.
E somente então dirá:
— Agora podemos conversar.
Dr. Strangelove sorrirá.
O mainframe continuará funcionando.
E ninguém precisará apertar o botão vermelho.
CHANGE REQUEST CLOSED.
INCIDENT NOT FOUND.
CAFÉ CONSUMIDO COM SUCESSO.
CÂMBIO FINAL.
Sem comentários:
Enviar um comentário