SQL de teste de unidade usando dbt

Dec 16 2022
Depois de anos trabalhando em ciência e engenharia de dados, a qualidade dos dados é o fantasma que aparece em quase todos os projetos, dizimando a realização dos negócios. SQL é a linguagem de fato dos dados.

Depois de anos trabalhando em ciência e engenharia de dados, a qualidade dos dados é o fantasma que aparece em quase todos os projetos, dizimando a realização dos negócios.

SQL é a linguagem de fato dos dados. Uma maneira de melhorar a qualidade dos dados é aprimorar a base de código SQL com teste de unidade e teste de dados. Este artigo é inspirado principalmente pelo post da Sra. Gao .

Neste artigo, a lógica fundamental do teste de unidade em SQL com dbt será ilustrada com um conjunto de dados simples.

Ideia básica

A ideia básica de realizar um teste de unidade em SQL é exatamente a mesma de fazer um teste de unidade em código Python:

  1. simular uma entrada controlável D
  2. existe um módulo/função/algoritmo testável , chame-o de A
  3. calcule o resultado esperado usando D como entrada, obtenha O_deve
  4. compare o resultado esperado (O_should) com a saída real de A (O_is)

Às vezes, não precisa ser uma correspondência exata, ou seja, corresponder apenas até 2 dígitos após o ponto decimal.

Um exemplo

Vamos construir um exemplo ingênuo, para ilustrar a ideia acima. Precisamos da seguinte estrutura de pastas do projeto dbt

-- dbt_project.yml

-- data/
------ iris.csv
------ selected_iris_expected.csv

-- models/
------ iris/
---------- selected_iris.sql
---------- schema.yml

# example: dbt_project.yml

# take things under data/ as seeds
data-paths: ["data"]

# configure seed, all going into unittesting schema
seeds:
    schema: unittesting

-- selected_iris.sql
{{ config(
        materialized='table',
        schema='unittesting',
        tags=['iris']
    )
}}

-- count the number of special iris id (above average in all aspects)
-- not a very meaningful logic, just for exemplare purpose
SELECT
distinct count(distinct id)
FROM "public"."iris";
where sepallengthcm > 5.9 and sepalwidthcm > 3.1 and petallengthcm > 3.8 and petalwidthcm > 1.2
-- selected_iris_expected.csv
count
150

# schema.yml 

version: 2

# table model selected_iris should be equal to iris
models:
  - name: selected_iris

    tests:
      - dbt_utils.equality:
          compare_model: ref('selected_iris_expected')
          tags: ['unit_testing']

Etapa 1: carregar dados de teste no banco de dados

# here we use
# iris.csv will be loaded into unittesting.iris table
# selected_iris_expected.csv will be loaded into unittesting.selected_iris_expected table
dbt seed
# build selected_iris model into unittesting.selected_iris
dbt run

afirmar a==b

Passo 2: compare

# here we use
# all tests within folder model/iris/ with be executed
# of course, we can restrict to only unittesting using tags
dbt test --model iris

tecnicalidades

O teste de unidade SQL parece simples, certo? mas na realidade a coisa poderia ser mais complexa:

  • você provavelmente precisa de uma estrutura de pastas mais sofisticada (usando subpastas aninhadas) para separar e organizar os testes de acordo com os projetos
  • pode ser necessário ativar/desativar o teste de unidade com base no ambiente atual dev/prod
  • você pode ter uma grande tabela de entrada que é difícil de zombar
  • você pode ter um modelo complexo, sobre o qual é difícil calcular o resultado esperado de antemão (modelo não testável)
  • o resultado esperado pode não corresponder 100% à saída real (mesmo que sejam praticamente os mesmos) devido à precisão do número flutuante, etc.
  • ou você pode simplesmente não ter orçamento de tempo neste projeto, o que não é incomum, as pessoas não testam muito o sql em 2022, as instruções SQL são consideradas corretas após terem sido escritas

Uma dica: modularizar instruções SQL

Apesar de todos os desafios listados acima, uma coisa ajuda no teste de unidade no caso do SQL, bem como em qualquer outra linguagem de programação mais geral, como o Python.

Isso é modularização. Um bom modelo/função/algoritmo modularizado garante a testabilidade e legibilidade, mesmo para sql.

Existem muitas maneiras de perceber isso com sql e dbt:

  • macro dbt (https://docs.getdbt.com/docs/build/jinja-macros)
  • com declaração (https://learnsql.com/blog/what-is-with-clause-sql/)

Conclusão

SQL é a linguagem nativa dos dados, neste artigo, demonstramos uma maneira de fazer testes de unidade SQL com dbt.

Da mesma forma, também poderíamos fazer testes de integração de dados ou outros truques, como delegar a criação de sql para uma linguagem mais poderosa, como python, usando rasgoQ L.