Em desenvolvimento de software, o termo TDD — Test Driven Development — é bastante conhecido e sua aplicação já está bem consolidada.
O termo nasceu em meados de 1990, pelo criador da Programação extrema, Kent Beck.
Atualmente esta prática é um dos principais meios de se manter uma aplicação saudável. Os testes diminuem muito — não extinguem — os riscos de quebrar a aplicação.
E será que é possível aplicar TDD em um pipeline de dados? É o que vamos ver a seguir. Já adianto que sim, é totalmente possível.
Primeiramente, o que é TDD?
Test Driven Development, ou em tradução livre, Desenvolvimento Guiado por Testes é uma estratégia de programação que consiste em criar situações ou respostas previamente conhecidas antes de desenvolver a aplicação em si.
De forma bem simplificada, para garantir que uma função saiba somar corretamente, criamos situações como:
- Se a função receber 2 e 3 como argumentos, deve retornar 5;
- Se a função receber 1, 2 e 3 como argumentos, deve retornar 6.
Ao invés de entrar de vez no desenvolvimento da aplicação, você cria cenários que testam o que ainda vai ser desenvolvido. Através de classes, métodos e/ou funções.
E como seria TDD em um PIPELINE de Dados?
Antes de tudo, seria bom saber o porquê aplicarmos TDD na engenharia de dados.
A motivação original de se desenvolver primeiro o cenário e depois a solução em si é composta por 3 pontos, os quais considero como principais:
- Sua função — ou classe — irá fazer APENAS o que for necessário para passar no teste. Tornando o código mais limpo e sem excesso de código inútil;
- Automatização de testes que são executados de forma muito mais rápida que qualquer humano;
- Evita que novas funcionalidades quebrem o que já estava funcionando.
Agora imagine um pipeline de dados, onde informações são extraídas das mais diversas formas, passam por uma série de processos que tratam e depois os inserem em uma base de destino.
Não sei você, mas todo o fluxo, quanto mais complexo, mais traz uma fragilidade intrínseca. Se no meio do caminho, qualquer coisa falhar, pode causar um enorme estrago no processo todo.
Lembra um pouco a Teoria do Caos e da célebre frase de Edward Lorenz:
“o bater das asas de uma borboleta no Brasil pode gerar um furacão no Texas!”.
Se uma das fontes de dados alterar a sua estrutura, pode acabar derrubando todo o fluxo, ou gerar resultados totalmente inesperados.
Então, para amenizar estes riscos, nada melhor do que utilizar uma estratégia já consolidada no mundo de desenvolvimento de software…https://operar.io/media/19c7128e5394209ed77b923fdd33dcc1
E como aplicar TDD com dados?
Leia a afirmação a seguir, vai entender onde quero chegar…
- Quando estamos para criar uma classe/função, testamos se com os parâmetros que ela recebe, retorna o resultado esperado.
O negrito nas palavras —classe/função, parâmetros e resultado — é proposital.
Mudando para dados, ficaria assim:
- Quando estamos para criar uma etapa do processo, testamos se com os dados que ela recebe, se ela retorna a estrutura esperada.
Ou seja, cada etapa é avaliada se retorna a estrutura esperada, tendo como entrada uma outra estrutura. Percebe que o termo “ambiente controlado” se encaixa perfeitamente aqui?
E o que podemos colocar em cada estrutura de entrada?
Para quem está acostumado(a) com TDD, conhece muito bem os corner cases.
Corner Cases em dados
Corner Case — ou Casos de “canto” — se refere a situações “limites” que seus testes devem cobrir.
Voltando ao começo, no exemplo da nossa função que deve somar, quais seriam os casos limites?
Alguns exemplos:
- Se receber apenas um número, deve retornar ele mesmo;
- Deve conseguir processar qualquer número de entrada, somar 1, 2, 3… números;
- Se receber um número negativo, deve retornar o resultado correto;
- Se receber 0 como entrada, o resultado não deve ser afetado.
A primeira etapa para o desenvolvimento é a criação das situações limites que a função deve receber.
Com dados, é mais ou menos a mesma coisa. Basta pensar qual tipo de corner case pode ter em uma estrutura de dados.
Vejamos alguns exemplos:
- Se a fonte for um arquivo csv, a etapa deve reconhecer o separador de colunas — como vírgula, ponto e vírgula e tabulação;
- No mesmo arquivo csv, a etapa deve reconhecer corretamente o tipo de cada coluna, se é de inteiros, strings, booleanos, etc.;
- A possibilidade de receber um csv vazio;
- Se for um formato json, com diferentes tipos de chaves além das de interesse do processo;
- Um formato json, com a ausência de uma ou mais chaves de interesse;
- Retorno de uma API com erros de autorização, timeout, etc.
Percebem que as possibilidades são muitas? Pois é, e quanto mais seu pipeline estiver preparado para lidar com cada uma destas situações, mais robusto ele será. Quanto maior a cobertura de possibilidades, menor o risco de quebrar o fluxo.
Simplicidade, esta é a palavra-chave
Quem já ouviu falar sobre kiss?
Não, não é beijo em inglês e nem a banda de rock novaiorquina.
Kiss é a sigla para “Keep it simple, stupid!”
Traduzindo, com o perdão da ofensa, “Deixe isso simples, idiota!”.
Trazendo para os nossos dados, quer dizer, deixe cada situação prevista nos corner cases a mais simples possível.
Não é preciso criar um csv com 1000 linhas para descrever uma estrutura esperada.
Quanto menor a estrutura esperada, seja ela um csv, json ou xml de uma API, melhor. Mais fácil de analisar, mais fácil de fazer uma possível manutenção no futuro. O único critério é que a estrutura descreva corretamente o que a etapa pode receber como input de dados.
Conclusão
Hoje, com o aumento de dados disponíveis que influenciam a tomada de decisão de qualquer negócio, é possível criar fluxogramas para a extração, tratamento e leitura destes dados com muito mais facilidade que a anos atrás.
No entanto, este processo é complexo e que pode ser facilmente interrompido quando qualquer etapa neste meio se quebra. É determinante que cada etapa seja coberta por testes que assegurem o comportamento esperado, a fim de não fragilizar o sistema como um todo.
Espero que vocês tenham gostado, qualquer dúvida, deixe nos comentários abaixo.