Pruebas en DBT: Test de datos y unitarios

La realización de test es un proceso que siempre es necesario para asegurarnos de que el comportamiento que esperamos de una aplicación es también el comportamiento real. Sin las pruebas apropiadas, subir un cambio puede provocar cierta inseguridad, ya que es difícil confirmar que toda la lógica sigue operando de la misma forma. Todo cambio, por menor que sea, altera la lógica implementada y por eso es necesario tener garantías. 

A menudo suele ocurrir que todo el proceso de testing se hace de forma manual: desde la creación de los escenarios hasta la validación de los resultados. Sin embargo, esto puede suponer un problema si otra persona que altere ese mismo código tiene que recrear dichos escenarios. Incluso puede que no reproduzca toda la lógica que ha de probarse para ese código. Por lo tanto, siempre es importante tener pruebas integradas dentro del propio código permitiendo así el testeo constante.

Para implementar estos test, DBT ofrece varias herramientas que mencionaremos a lo largo de este post. Aunque, principalmente, veremos dos tipos: los test sobre la calidad del dato y los test unitarios, que se prueban mediante el uso del comando dbt test.

Test de datos

Esta tipología de test no comprueba si la lógica implementada es correcta o no, o si sabe tratar casos anómalos correctamente. Aquí, lo que se prueba es la calidad del dato. Por eso, simplemente son suposiciones sobre los mismos, como por ejemplo que una columna nunca puede ser nula, que un ID debe ser único, etc.

Existen dos tipos de tests de datos en DBT: los singulares y los genéricos.

Test de datos singulares

Los test singulares son una simple confirmación hecha con SQL. Son útiles para probar aserciones específicas a la lógica de un modelo singular. Si esta lógica es compartida, no es recomendable usar tests singulares, ya que sería necesario escribir un test diferente para cada modelo.

SELECT *
FROM {{ ref(model_name) }}
WHERE random_column IS NULL AND random_column_2 = 10 

Este test fallará cuando, en nuestro modelo, tengamos que random_column es nula y a la vez, random_column_2 es 10 en cualquier registro.

Test de datos genéricos

Los test genéricos son pruebas que se definen con parámetros para permitir su reutilización en varios modelos. Esto es extremadamente útil cuando, por ejemplo, varias columnas comparten la misma lógica en diferentes modelos.

Los parámetros esenciales son model y column_name, refiriéndose al modelo y la columna respectivamente.

{% test test_between_valid_ranges(model, column_name) %}
   select *
   from {{ model }}
   where {{ column_name }} < 0 and {{ column_name }} > 100
{% endtest %}

Para usar este test, es necesario definir en el .yml del modelo sobre qué columna se aplica e indicar su nombre.

models:
 - name: model_name
   columns:
     - name: percent
       tests:
         - test_between_valid_ranges

Para añadir más argumentos, es necesario pasarlos como diccionario en la definición del test en el .yml.

models:
 - name: model_name
   columns:
     - name: random_column
       tests:
         - test_name:
             other_argument1: valid_value_1
             other_argument2: valid_value_2

Los nuevos parámetros declarados los podemos incluir en el test.

{% test test_name(model, column_name, other_argument1, other_argument2) %}
   select *
   from {{ model }}
   where {{ column_name }} not in (other_argument1,other_argument2)
{% endtest %}

Test Unitarios

Los test unitarios son útiles en casos en los que queremos comprobar que la lógica implementada en el modelo representa la lógica deseada. Dado que estamos comprobando la lógica, no se tiene en cuenta el estado actual de los modelos. Lo que nos importa es que dichos modelos sean transformados como deseamos. Por eso, a diferencia de los test de datos, solo necesitamos los datos de entrada y los datos de salida del modelo. 

Los datos pueden ser dados en diversos formatos: SQL, CSV, diccionario, etc. Sin embargo, en el caso de testear modelos efímeros, únicamente se puede usar SQL como dato de entrada.

CSV

id, location, price
'random_id', amount, 25

SQL

select 'random_id' as id, 'random_location' as location, 25 as amount

Diccionario

{id: 'random_id', location: 'random_location', amount: 25}

Para este ejemplo, indicamos el input en formato JSON.

unit_tests:
 - name: test_metric_average
   description: "Check if average metric is computed correctly"
   model: dim_customers
   given:
     - input: ref('stg_customers')
       rows:
         - {id:1, amount:10}
         - {id:1, amount:15}
         - {id:2, amount:10}
         - {id:2, amount:14}
   expect:
     rows:
       - {id:1, amount:12.5}
       - {id:1, amount:12}
Conclusión

En este artículo, hemos visto maneras diferentes de implementar tests en DBT: los de calidad del dato y los unitarios. Estas pruebas nos permiten tener una mayor seguridad sobre el proceso, asegurándonos de que algunos comportamientos siempre se han de cumplir. Además, facilitan el cambio y desarrollo de los procesos sin la preocupación de que algún elemento haya cambiado su comportamiento deseado.

Si te ha parecido interesante este artículo, visita la categoría Data Engineering de nuestro blog para ver post similares a este y compártelo en redes con todos tus contactos. No olvides mencionarnos para poder conocer tu opinión @Damavisstudio. ¡Hasta pronto!
Antoni Casas
Antoni Casas
Artículos: 18