Lidando com o Caos: testando
código PLSQL em projeto crítico
Preparar essa
palestra me trouxe
de volta um
sentimento…
INSEGURANÇA
parece bobagem, mas
não é…
Imagina entrar num
projeto…
CRÍTICO
PROJETO
Reestruturar Logistica
Integração de
vários sistemas
Prazo
Apertado
Onde você…
NADA do
NEGÓCIO
NEM dos
SISTEMAS
ENVOLVIDOS
Tabelas e mais Tabelas
Pra piorar…
Não
2006
2006+10 anos
Mas daí…
Me deram a BOA
Notícia!
Líder Técnico
E quando fomos
apresentados…
Olá, tudo bom? Meu nome Rafael. Novo
desenvolvedor no projeto.
Olá, tudo bom? Meu nome Rafael. Novo
desenvolvedor no projeto.
Opa, sou Dudu. O líder técnico do projeto.
Olá, tudo bom? Meu nome Rafael. Novo
desenvolvedor no projeto.
Opa, sou Dudu. O líder técnico do projeto.
300, viu?
Olá, tudo bom? Meu nome Rafael. Novo
desenvolvedor no projeto.
Opa, sou Dudu. O líder técnico do projeto.
300, viu?
ahn!!? 300? 300 o que?
Olá, tudo bom? Meu nome Rafael. Novo
desenvolvedor no projeto.
Opa, sou Dudu. O líder técnico do projeto.
300, viu?
ahn!!? 300? 300 o que?
R$300.000 por hora se o sistema
parar!
R$300k/h
E assim eu me senti
de novo…
ESTAGIÁRIO
Me veio vários
questionamentos…
Será que posso
ajudar no projeto?
Minha experiência
vai ajudar em algo?
Lidando com o Caos: testando
código PLSQL em projeto crítico
@rponte
Eduardo
Menezes
Lidando com o Caos: testando
código PLSQL em projeto crítico
Esclarecendo o que
o Rafael falou…
Líder Técnico
Líder Técnico
R$300k/h
Falei besteira pro
Rafael…
R$5.5mi/h
E a empresa na qual
rola o projeto é…
4.6bi/ano$ * Lucro Liquido - 2015
E este PROJETO tem
por finalidade…
Reestruturar Logistica
Utilizando
tecnologias como…
Mas sem dúvida…
Parte importante
dessa integração
ficou com…
Para quem não
conhece PL/SQL…
Integer tamanho(String texto) {
// retorna tamanho
return texto.length();
}
Java
function tamanho(texto varchar2)

return number is
begin
-- retorna tamanho
return lENGTH(texto);
end;
PL/SQL
Apesar de ser uma
linguagem simples
Quando o assunto é
PROCESSAR DADOS…
PODEROSA
Afinal…
function tamanho(texto varchar2)

return number is
begin
-- retorna tamanho
return lENGTH(texto);
end;
E na MDIAS
trabalhamos com
GRANDES VOLUMES
DE DADOS
Não dá pra
ignorar isso!
PERFORMANCE
Mas PL/SQL tem
seus problemas…
Milhares de Linhas
Fácil ter procedures
com 5-10k linhas de
código
Ferramentas Precárias
Esqueça IDEs
sofisticadas como
Eclipse
Regras de Negócio
Não tem a mesma
clareza de linguagens
OO
E isso era um grande
problema…
Regras de Negócio
Complexas
Pra piorar…
A metade da equipe
não dominava o
negócio
Agora imagina…
PL/SQL
PL/SQL
+
PL/SQL
+Regras 

Complexas
Batemos com 2
desafios sérios…
MANUTENÇÃO

do código
CORRETUDE 

das regras de negócio
CORRETUDE 

das regras de negócio
Como representar as
regras em código
corretamente?
Documentação
Quadro-branco
Programação em Par
Programação em Par
MANUTENÇÃO

do código
Como escrever código
de qualidade?
Como escrever código
fácil de ler e manter?
Guia de Estilo
Padronização do Código
Padronização ajuda;
estilo ajuda. Mas
precisamos ir mais
longe…
BEGIN
IF is_frete_calc(cliente) THEN

processa_entrega(cliente);
notifica_cliente(cliente);
END IF;
END;
Funções Pequenas
BEGIN
IF is_frete_calc(cliente) THEN

processa_entrega(cliente);
notifica_cliente(cliente);
END IF;
END;
Funções Pequenas
BEGIN
IF is_frete_calc(cliente) THEN

processa_entrega(cliente);
notifica_cliente(cliente);
END IF;
END;
Funções Pequenas
BEGIN
IF is_frete_calc(cliente) THEN

processa_entrega(cliente);
notifica_cliente(cliente);
END IF;
END;
Funções Pequenas
e claro…
BEGIN
IF is_frete_calc(cliente) THEN

processa_entrega(cliente);
notifica_cliente(cliente);
END IF;
END;
Funções Pequenas
comNOMESLEGÍVEIS!
BONS
NOMES
MAIOR
CLAREZA=
BONS
NOMES MENOS
COMENTÁRIOS
NO CÓDIGO
=
Só isso é suficiente?
Não
Precisamos
EXECUTAR o código
para ter certeza
Mas como executar?
Manualmente?
BEGIN


processa_entrega(cliente);
END;
ou…
Usa equipe de QA…
só que…
e ai?
Testes Automatizados
function tamanho(texto varchar2)

return number is
begin
-- retorna tamanho
return lENGTH(texto);
end;
Não existem boas
ferramentas de testes
Mas nosso time é…
function tamanho(texto varchar2)

return number is
begin
-- retorna tamanho
return lENGTH(texto);
end;
Executa procedure
Executa procedure
Devolve resultado
Executa procedure
Devolve resultado
PL/SQL
Calcular Frete
-- Calcula Frete
function calc_frete(uf varchar2)

return number is
begin
if uf = 'SP' then
return 30.0;
end if;
return 20.20;
end;
PL/SQL
-- Calcula Frete
function calc_frete(uf varchar2)

return number is
begin
if uf = 'SP' then
return 30.0;
end if;
return 20.20;
end;
PL/SQL
-- Calcula Frete
function calc_frete(uf varchar2)

return number is
begin
if uf = 'SP' then
return 30.0;
end if;
return 20.20;
end;
PL/SQL
-- Calcula Frete
function calc_frete(uf varchar2)

return number is
begin
if uf = 'SP' then
return 30.0;
end if;
return 20.20;
end;
PL/SQL
-- Calcula Frete
function calc_frete(uf varchar2)

return number is
begin
if uf = 'SP' then
return 30.0;
end if;
return 20.20;
end;
PL/SQL
-- Calcula Frete
function calc_frete(uf varchar2)

return number is
begin
if uf = 'SP' then
return 30.0;
end if;
return 20.20;
end;
PL/SQL
-- Calcula Frete
function calc_frete(uf varchar2)

return number is
begin
if uf = 'SP' then
return 30.0;
end if;
return 20.20;
end;
PL/SQL
-- Calcula Frete
function calc_frete(uf varchar2)

return number is
begin
if uf = 'SP' then
return 30.0;
end if;
return 20.20;
end;
PL/SQL
JAVA
Calcular Frete
class FreteService {
public Double calcula(String uf) {
String sql = "{? = call calc_frete(?)}”;
Connection c = // abre conexão
CallableStatement cs = c.prepareCall(sql);
cs.registerOutParameter(1, Types.VARCHAR);
cs.setString(2, uf);
cs.execute();
return cs.getDouble(1);
}
}
Java
class FreteService {
public Double calcula(String uf) {
String sql = "{? = call calc_frete(?)}”;
Connection c = // abre conexão
CallableStatement cs = c.prepareCall(sql);
cs.registerOutParameter(1, Types.VARCHAR);
cs.setString(2, uf);
cs.execute();
return cs.getDouble(1);
}
}
Java
class FreteService {
public Double calcula(String uf) {
String sql = "{? = call calc_frete(?)}”;
Connection c = // abre conexão
CallableStatement cs = c.prepareCall(sql);
cs.registerOutParameter(1, Types.VARCHAR);
cs.setString(2, uf);
cs.execute();
return cs.getDouble(1);
}
}
Java
class FreteService {
public Double calcula(String uf) {
String sql = "{? = call calc_frete(?)}";
Connection c = // abre conexão
CallableStatement cs = c.prepareCall(sql);
cs.registerOutParameter(1, Types.VARCHAR);
cs.setString(2, uf);
cs.execute();
return cs.getDouble(1);
}
}
Java
class FreteService {
public Double calcula(String uf) {
String sql = "{? = call calc_frete(?)}";
Connection c = // abre conexão
CallableStatement cs = c.prepareCall(sql);
cs.registerOutParameter(1, Types.VARCHAR);
cs.setString(2, uf);
cs.execute();
return cs.getDouble(1);
}
}
Java
class FreteService {
public Double calcula(String uf) {
String sql = "{? = call calc_frete(?)}";
Connection c = // abre conexão
CallableStatement cs = c.prepareCall(sql);
cs.registerOutParameter(1, Types.DOUBLE);
cs.setString(2, uf);
cs.execute();
return cs.getDouble(1);
}
}
Java
class FreteService {
public Double calcula(String uf) {
String sql = "{? = call calc_frete(?)}";
Connection c = // abre conexão
CallableStatement cs = c.prepareCall(sql);
cs.registerOutParameter(1, Types.DOUBLE);
cs.setString(2, uf);
cs.execute();
return cs.getDouble(1);
}
}
Java
class FreteService {
public Double calcula(String uf) {
String sql = "{? = call calc_frete(?)}";
Connection c = // abre conexão
CallableStatement cs = c.prepareCall(sql);
cs.registerOutParameter(1, Types.DOUBLE);
cs.setString(2, uf);
cs.execute();
return cs.getDouble(1);
}
}
Java
class FreteService {
public Double calcula(String uf) {
String sql = "{? = call calc_frete(?)}";
Connection c = // abre conexão
CallableStatement cs = c.prepareCall(sql);
cs.registerOutParameter(1, Types.DOUBLE);
cs.setString(2, uf);
cs.execute(); // executa function
return cs.getDouble(1);
}
}
Java
class FreteService {
public Double calcula(String uf) {
String sql = "{? = call calc_frete(?)}";
Connection c = // abre conexão
CallableStatement cs = c.prepareCall(sql);
cs.registerOutParameter(1, Types.DOUBLE);
cs.setString(2, uf);
cs.execute(); // executa function
return cs.getDouble(1);
}
}
Java
class FreteService {
public Double calcula(String uf) {
String sql = "{? = call calc_frete(?)}";
Connection c = // abre conexão
CallableStatement cs = c.prepareCall(sql);
cs.registerOutParameter(1, Types.DOUBLE);
cs.setString(2, uf);
cs.execute(); // executa function
return cs.getDouble(1);
}
}
Java
jUnit
Testar Calcular Frete
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
jUnitclass FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
jUnit
Vamos complicar?
PL/SQL
Calcular Frete
-- Calcula Frete
function calc_frete(uf varchar2)

return number is
begin
if uf = 'SP' then
return 30.0;
end if;
return 20.20;
end;
PL/SQL
-- Calcula Frete
function calc_frete(uf varchar2)

return number is
begin
if uf = 'SP' then
return 30.0;
end if;
return 20.20;
end;
PL/SQL
function calc_frete(uf varchar2)

return number is
begin
-- nova logica
end;
PL/SQL
MODELO
function calc_frete(uf varchar2) return number
is
valor_frete number;
begin
-- busca valor do frete na tabela
SELECT f.valor
INTO valor_frete
FROM TB_FRETE_VALOR f
WHERE f.uf_destino = uf;
return valor_frete;
end;
PL/SQL
function calc_frete(uf varchar2) return number
is
valor_frete number;
begin
-- busca valor do frete na tabela
SELECT f.valor
FROM TB_FRETE_VALOR f
WHERE f.uf_destino = uf;
return valor_frete;
end;
PL/SQL
function calc_frete(uf varchar2) return number
is
valor_frete number;
begin
-- busca valor do frete na tabela
SELECT f.valor
FROM TB_FRETE_VALOR f
WHERE f.uf_destino = uf;
return valor_frete;
end;
PL/SQL
function calc_frete(uf varchar2) return number
is
valor_frete number;
begin
-- busca valor do frete na tabela
SELECT f.valor
INTO valor_frete
FROM TB_FRETE_VALOR f
WHERE f.uf_destino = uf;
return valor_frete;
end;
PL/SQL
function calc_frete(uf varchar2) return number
is
valor_frete number;
begin
-- busca valor do frete na tabela
SELECT f.valor
INTO valor_frete
FROM TB_FRETE_VALOR f
WHERE f.uf_destino = uf;
return valor_frete;
end;
PL/SQL
function calc_frete(uf varchar2) return number
is
valor_frete number;
begin
-- busca valor do frete na tabela
SELECT f.valor
INTO valor_frete
FROM TB_FRETE_VALOR f
WHERE f.uf_destino = uf;
return valor_frete;
end;
PL/SQL
E o código Java?
class FreteService {
public Double calcula(String uf) {
String sql = "{? = call calc_frete(?)}";
Connection c = // abre conexão
CallableStatement cs = c.prepareCall(sql);
cs.registerOutParameter(1, Types.DOUBLE);
cs.setString(2, uf);
cs.execute(); // executa function
return cs.getDouble(1);
}
}
Java
class FreteService {
public Double calcula(String uf) {
String sql = "{? = call calc_frete(?)}";
Connection c = // abre conexão
CallableStatement cs = c.prepareCall(sql);
cs.registerOutParameter(1, Types.DOUBLE);
cs.setString(2, uf);
cs.execute(); // executa function
return cs.getDouble(1);
}
}
Java
Nada!
jUnit
Testar Calcular Frete
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
jUnit
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
this.limpaEInsereFrete("SP", 30.0);
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
this.limpaEInsereFrete("SP", 30.0);
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
this.limpaEInsereFrete("SP", 30.0);
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
DELETEFROMTB_FRETE_VALOR;

INSERTINTO TB_FRETE_VALORVALUES(1,‘SP’,30.30);
class FreteServiceTest {
@Test
public void deveCalcularFrete() {
// cenário
String uf = "SP";
this.limpaEInsereFrete("SP", 30.0);
// ação
FreteService service = new FreteService();
double valor = service.calcula(uf);
// validação
assertEquals(30.0, valor);
}
}
jUnit
jUnit
E aí, tudo testado?
Mando pra produção?
E SEnão encontrar
a UF na tabela?
function calc_frete(uf varchar2) return number
is
valor_frete number;
begin
-- busca valor do frete na tabela
SELECT f.valor
INTO valor_frete
FROM TB_FRETE_VALOR f
WHERE f.uf_destino = uf;
return valor_frete;
end;
PL/SQL
function calc_frete(uf varchar2) return number is
valor_frete number;
begin
-- busca valor do frete na tabela
SELECT f.valor
INTO valor_frete
FROM TB_FRETE_VALOR f
WHERE f.uf_destino = uf;
return valor_frete;
EXCEPTION
WHEN no_data_found THEN
raise_application_error(-20200, ‘oops!’);
end;
PL/SQL
class FreteServiceTest {
@Test
public void
naoDeveCalcularFreteQuandoUfNaoEncontrada()
{
// lógica do teste vai aqui
}
}
jUnit
jUnit
jUnit
E SEencontrar
mais de um frete?
E SE…
Outros Cenários
Desafios
Desconfiança
Pair
Programming
Testes
Automatizados
O pior…
ATRASOU
mesmo!
Mas não foi à toa…
não tem LITERATURA
não tem COMUNIDADE
não tem CULTURA
Não se engane!
Tivemos bons
resultados!
Melhoramos
nosso processo
Simplificamos nosso
ambiente
Diminuimos o
indice de bugs
Não temos
MEDO de mexer
no código
Padronização da
Arquitetura dos testes
Agora a Gerência
gostou!!
e a partir de agora
novos projetos irão
adotar esta
metodologia
o que ficou faltando?
treinar restante da
equipe
Servidor
de
Integração
Cobrir procedures
IMPORTANTES com
testes
Bem…
O caminho é longo
Não será fácil…
Nem acontecerá da
noite pro dia…
Obrigado!
Eduardo Menezes

edumenezes@gmail.com
Rafael Ponte

rponte@gmail.com

Lidando com o Caos: Testando Código PLSQL em um Projeto Critico