Lidando com o Caos: Testando Código PL/SQL em um Projeto Critico

Lidando com o Caos: Testando Código PL/SQL em um Projeto Critico

Imagine entrar em um projeto critico de uma grande empresa onde todo o desenvolvimento se baseia em integrar diversos outros sistemas mais criticos ainda? Agora, imagine que essa integração ocorrerá toda via banco de dados através de stored procedures, de código PLSQL?

São milhares de linhas de código PLSQL executando toda lógica de negócio que deve rodar com alto desempenho e, principalmente, corretude. Uma simples falha e todo um sistema de Logística pode parar! Como garantir que tudo funciona de forma correta e mais, que tenhamos ao final um código manutenível? Nessa apresentação vamos expor como nossa equipe conseguiu implementar todo esse código PLSQL com qualidade e o melhor de tudo, coberto por centenas de testes automatizados; o uso de testes foi crucial para o sucesso! Some a isso todo o desafio cultural e desconfiança do mercado em testar código PLSQL, a falta de literatura sobre o assunto e a real necessidade de automatizar ao máximo o ambiente de desenvolvimento.

Essa palestra te fará repensar sobre como você programa em PLSQL.

Palestra ministrada no evento Javou #9 da JavaCE em MAR-2017 (http://www.javace.org/javou-09-casos-de-sucesso/) por Rafael Ponte e Eduardo Menezes (edumenezes@gmail.com)

F853760c988228c4a153333407e64f09?s=128

Rafael Ponte

March 04, 2017
Tweet

Transcript

  1. Lidando com o Caos: testando código PLSQL em projeto crítico

  2. Preparar essa palestra me trouxe de volta um sentimento…

  3. INSEGURANÇA

  4. parece bobagem, mas não é…

  5. Imagina entrar num projeto…

  6. CRÍTICO PROJETO

  7. Reestruturar Logistica

  8. Integração de vários sistemas

  9. Prazo Apertado

  10. Onde você…

  11. NADA do NEGÓCIO

  12. None
  13. NEM dos SISTEMAS ENVOLVIDOS

  14. Tabelas e mais Tabelas

  15. Pra piorar…

  16. None
  17. Não

  18. None
  19. 2006

  20. 2006 +10 anos

  21. Mas daí…

  22. Me deram a BOA Notícia!

  23. Líder Técnico

  24. E quando fomos apresentados…

  25. Olá, tudo bom? Meu nome Rafael. Novo desenvolvedor no projeto.

  26. Olá, tudo bom? Meu nome Rafael. Novo desenvolvedor no projeto.

    Opa, sou Dudu. O líder técnico do projeto.
  27. Olá, tudo bom? Meu nome Rafael. Novo desenvolvedor no projeto.

    Opa, sou Dudu. O líder técnico do projeto. 300, viu?
  28. 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?
  29. 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!
  30. R$300k/h

  31. None
  32. E assim eu me senti de novo…

  33. ESTAGIÁRIO

  34. None
  35. None
  36. Me veio vários questionamentos…

  37. Será que posso ajudar no projeto?

  38. Minha experiência vai ajudar em algo?

  39. Lidando com o Caos: testando código PLSQL em projeto crítico

  40. @rponte

  41. None
  42. Eduardo Menezes

  43. None
  44. Lidando com o Caos: testando código PLSQL em projeto crítico

  45. Esclarecendo o que o Rafael falou…

  46. Líder Técnico

  47. Líder Técnico

  48. R$300k/h

  49. Falei besteira pro Rafael…

  50. R$5.5mi/h

  51. E a empresa na qual rola o projeto é…

  52. None
  53. None
  54. 4.6bi/ano $ * Lucro Liquido - 2015

  55. E este PROJETO tem por finalidade…

  56. Reestruturar Logistica

  57. None
  58. Utilizando tecnologias como…

  59. None
  60. None
  61. None
  62. None
  63. None
  64. None
  65. Mas sem dúvida…

  66. Parte importante dessa integração ficou com…

  67. None
  68. Para quem não conhece PL/SQL…

  69. Integer tamanho(String texto) { // retorna tamanho return texto.length(); }

    Java
  70. function tamanho(texto varchar2)
 return number is begin -- retorna tamanho

    return lENGTH(texto); end; PL/SQL
  71. Apesar de ser uma linguagem simples

  72. Quando o assunto é PROCESSAR DADOS…

  73. PODEROSA

  74. Afinal…

  75. function tamanho(texto varchar2)
 return number is begin -- retorna tamanho

    return lENGTH(texto); end;
  76. E na MDIAS trabalhamos com GRANDES VOLUMES DE DADOS

  77. Não dá pra ignorar isso!

  78. PERFORMANCE

  79. Mas PL/SQL tem seus problemas…

  80. Milhares de Linhas

  81. Fácil ter procedures com 5-10k linhas de código

  82. Ferramentas Precárias

  83. None
  84. Esqueça IDEs sofisticadas como Eclipse

  85. Regras de Negócio

  86. Não tem a mesma clareza de linguagens OO

  87. E isso era um grande problema…

  88. Regras de Negócio Complexas

  89. Pra piorar…

  90. A metade da equipe não dominava o negócio

  91. Agora imagina…

  92. PL/SQL

  93. PL/SQL +

  94. PL/SQL +Regras 
 Complexas

  95. Batemos com 2 desafios sérios…

  96. MANUTENÇÃO
 do código CORRETUDE 
 das regras de negócio

  97. CORRETUDE 
 das regras de negócio

  98. Como representar as regras em código corretamente?

  99. Documentação

  100. Quadro-branco

  101. Programação em Par

  102. Programação em Par

  103. MANUTENÇÃO
 do código

  104. Como escrever código de qualidade?

  105. Como escrever código fácil de ler e manter?

  106. Guia de Estilo

  107. Padronização do Código

  108. Padronização ajuda; estilo ajuda. Mas precisamos ir mais longe…

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

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

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

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

    Pequenas
  113. e claro…

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

    Pequenas com NOMES LEGÍVEIS!
  115. BONS NOMES MAIOR CLAREZA =

  116. BONS NOMES MENOS COMENTÁRIOS NO CÓDIGO =

  117. Só isso é suficiente?

  118. Não

  119. Precisamos EXECUTAR o código para ter certeza

  120. Mas como executar?

  121. Manualmente?

  122. BEGIN 
 processa_entrega(cliente); END;

  123. None
  124. ou…

  125. Usa equipe de QA…

  126. só que…

  127. None
  128. e ai?

  129. Testes Automatizados

  130. function tamanho(texto varchar2)
 return number is begin -- retorna tamanho

    return lENGTH(texto); end;
  131. Não existem boas ferramentas de testes

  132. Mas nosso time é…

  133. None
  134. None
  135. function tamanho(texto varchar2)
 return number is begin -- retorna tamanho

    return lENGTH(texto); end;
  136. None
  137. None
  138. Executa procedure

  139. Executa procedure Devolve resultado

  140. Executa procedure Devolve resultado

  141. None
  142. PL/SQL Calcular Frete

  143. -- 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
  144. -- 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
  145. -- 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
  146. -- 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
  147. -- 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
  148. -- 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
  149. -- 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
  150. -- 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
  151. JAVA Calcular Frete

  152. 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
  153. 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
  154. 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
  155. 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
  156. 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
  157. 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
  158. 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
  159. 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
  160. 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
  161. 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
  162. 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
  163. jUnit Testar Calcular Frete

  164. 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
  165. 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
  166. 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
  167. 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
  168. 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
  169. 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); } }
  170. 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
  171. 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
  172. 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
  173. 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
  174. 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
  175. 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
  176. 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
  177. jUnit

  178. Vamos complicar?

  179. PL/SQL Calcular Frete

  180. -- 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
  181. -- 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
  182. function calc_frete(uf varchar2)
 return number is begin -- nova logica

    end; PL/SQL
  183. MODELO

  184. 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
  185. 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
  186. 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
  187. 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
  188. 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
  189. 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
  190. E o código Java?

  191. 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
  192. 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!
  193. jUnit Testar Calcular Frete

  194. 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
  195. jUnit

  196. 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
  197. 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
  198. 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 DELETE FROM TB_FRETE_VALOR;
 INSERT INTO TB_FRETE_VALOR VALUES(1, ‘SP’, 30.30);
  199. 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
  200. jUnit

  201. E aí, tudo testado?

  202. Mando pra produção?

  203. E SE não encontrar a UF na tabela?

  204. 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
  205. 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
  206. class FreteServiceTest { @Test public void naoDeveCalcularFreteQuandoUfNaoEncontrada() { // lógica

    do teste vai aqui } } jUnit
  207. jUnit

  208. jUnit

  209. E SE encontrar mais de um frete?

  210. E SE…

  211. Outros Cenários

  212. Desafios

  213. Desconfiança

  214. Pair Programming

  215. Testes Automatizados

  216. O pior…

  217. ATRASOU mesmo!

  218. Mas não foi à toa…

  219. não tem LITERATURA

  220. não tem COMUNIDADE

  221. não tem CULTURA

  222. Não se engane!

  223. Tivemos bons resultados!

  224. Melhoramos nosso processo

  225. Simplificamos nosso ambiente

  226. Diminuimos o indice de bugs

  227. Não temos MEDO de mexer no código

  228. Padronização da Arquitetura dos testes

  229. Agora a Gerência gostou!!

  230. e a partir de agora novos projetos irão adotar esta

    metodologia
  231. o que ficou faltando?

  232. treinar restante da equipe

  233. Servidor de Integração

  234. None
  235. Cobrir procedures IMPORTANTES com testes

  236. Bem…

  237. O caminho é longo

  238. Não será fácil…

  239. Nem acontecerá da noite pro dia…

  240. Obrigado! Eduardo Menezes
 edumenezes@gmail.com Rafael Ponte
 rponte@gmail.com