Prueba de unidad SQL usando dbt

Dec 16 2022
Después de años de trabajar en ciencia e ingeniería de datos, la calidad de los datos es el fantasma colgante que aparece en casi todos los proyectos, diezmando los logros comerciales. SQL es el lenguaje de facto de los datos.

Después de años de trabajar en ciencia e ingeniería de datos, la calidad de los datos es el fantasma colgante que aparece en casi todos los proyectos, diezmando los logros comerciales.

SQL es el lenguaje de facto de los datos. Una forma de mejorar la calidad de los datos es mejorar la base de código SQL con pruebas unitarias y pruebas de datos. Este artículo está inspirado principalmente en la publicación de la Sra. Gao .

En este artículo, la lógica fundamental de la prueba unitaria en SQL con dbt se ilustrará con un conjunto de datos simple.

Idea básica

La idea básica de realizar una prueba unitaria en SQL es exactamente la misma que hacer una prueba unitaria en el código de Python:

  1. burlarse de una entrada controlable D
  2. hay un módulo/función/algoritmo comprobable , llámelo A
  3. calcule el resultado esperado usando D como entrada, obtenga O_should
  4. compare el resultado esperado (O_should) con la salida real de A (O_is)

A veces no tiene que ser una coincidencia exacta, es decir, coincidir solo hasta 2 dígitos después del punto decimal.

Un ejemplo

Construyamos un ejemplo ingenuo para ilustrar la idea anterior. Necesitamos la siguiente estructura de carpetas del proyecto 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']

Paso 1: cargar datos de prueba en la base de datos

# 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

Paso 2: comparar

# 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

tecnicismos

Las pruebas unitarias de SQL parecen simples, ¿verdad? pero en realidad la cosa podría ser más compleja:

  • probablemente necesite una estructura de carpetas más sofisticada (usando subcarpetas anidadas) para separar y organizar las pruebas según los proyectos
  • es posible que deba activar/desactivar las pruebas unitarias en función del desarrollo/producción del entorno actual
  • es posible que tenga una tabla de entrada grande que es difícil de burlar
  • es posible que tenga un modelo complejo, sobre el cual es difícil calcular el resultado esperado de antemano (modelo no comprobable)
  • el resultado esperado puede no coincidir al 100% con la salida real (incluso si son prácticamente iguales) debido a la precisión del número flotante, etc.
  • o es posible que simplemente no tenga un presupuesto de tiempo en este proyecto, lo cual no es raro, las personas no prueban mucho la unidad sql en 2022, las declaraciones SQL se consideran correctas después de haber sido escritas

Un consejo: modularice las sentencias SQL

A pesar de todos los desafíos enumerados anteriormente, una cosa ayuda con las pruebas unitarias en el caso de SQL, así como cualquier otro lenguaje de programación más general como Python.

Eso es Modularización. Un buen modelo/función/algoritmo modularizado garantiza la capacidad de prueba y legibilidad, incluso para sql.

Hay muchas formas de realizar esto con sql y dbt:

  • macro dbt (https://docs.getdbt.com/docs/build/jinja-macros)
  • con declaración (https://learnsql.com/blog/what-is-with-clause-sql/)

Conclusión

SQL es el lenguaje nativo de los datos. En este artículo, demostramos una forma de realizar pruebas unitarias de SQL con dbt.

Del mismo modo, también podríamos hacer una prueba de integración de datos u otros trucos como delegar la creación de sql a un lenguaje más poderoso como python usando rasgoQ L.