Testar qualidade de dados

Neste documento, mostramos como testar o código do fluxo de trabalho com asserções de tabela e testes de unidade do Dataform.

Antes de começar

  1. No Google Cloud console, acesse a página Dataform.

    Acessar a página do Dataform

  2. Selecione ou crie um repositório.

  3. Selecione ou crie um espaço de trabalho de desenvolvimento.

  4. Criar uma tabela.

Funções exigidas

Para receber as permissões necessárias para criar asserções e testes de unidade, peça ao administrador que conceda a você os seguintes papéis do IAM:

Para mais informações sobre a concessão de papéis, consulte Gerenciar o acesso a projetos, pastas e organizações.

Também é possível conseguir as permissões necessárias usando papéis personalizados ou outros papéis predefinidos.

Dados de teste com asserções

Uma declaração é uma consulta de teste de qualidade de dados que encontra linhas que violam uma ou mais condições especificadas na consulta. Se a consulta retornar alguma linha, a declaração vai falhar. O Dataform executa declarações sempre que atualiza seu fluxo de trabalho e alerta você se alguma declaração falhar.

O Dataform cria automaticamente visualizações no BigQuery que contêm os resultados de consultas de declaração compiladas. Conforme configurado no arquivo de configurações do fluxo de trabalho, o Dataform cria essas visualizações em um esquema de asserções em que é possível inspecionar os resultados das asserções.

Por exemplo, para o esquema padrão dataform_assertions, o Dataform cria uma visualização no BigQuery no seguinte formato: dataform_assertions.assertion_name.

É possível criar asserções para todos os tipos de tabelas do Dataform: tabelas, tabelas incrementais, visualizações e visualizações materializadas.

É possível criar asserções das seguintes maneiras:

Criar declarações integradas

É possível adicionar declarações integradas do Dataform ao bloco config de uma tabela. O Dataform executa essas declarações depois da criação da tabela. Depois que o Dataform criar a tabela, você poderá verificar se a declaração foi aprovada na guia Registros de execução do fluxo de trabalho do seu espaço de trabalho.

É possível criar as seguintes declarações no bloco config de uma tabela:

  • nonNull

    Essa condição afirma que as colunas especificadas não são nulas em todas as linhas da tabela. Essa condição é usada para colunas que nunca podem ser nulas.

    O exemplo de código a seguir mostra uma declaração nonNull no bloco config de uma tabela:

config {
  type: "table",
  assertions: {
    nonNull: ["user_id", "customer_id", "email"]
  }
}
SELECT ...
  • rowConditions

    Essa condição afirma que todas as linhas da tabela seguem a lógica personalizada definida. Cada condição de linha é uma expressão SQL personalizada, e cada linha da tabela é avaliada em relação a cada condição de linha. A declaração falha se alguma linha da tabela resultar em false.

    O exemplo de código a seguir mostra uma declaração rowConditions personalizada no bloco config de uma tabela incremental:

config {
  type: "incremental",
  assertions: {
    rowConditions: [
      'signup_date is null or signup_date > "2022-08-01"',
      'email like "%@%.%"'
    ]
  }
}
SELECT ...
  • uniqueKey

    Essa condição afirma que, em uma coluna especificada, nenhuma linha da tabela tem o mesmo valor.

    O exemplo de código a seguir mostra uma declaração uniqueKey no bloco config de uma visualização:

config {
  type: "view",
  assertions: {
    uniqueKey: ["user_id"]
  }
}
SELECT ...
  • uniqueKeys

    Essa condição afirma que, nas colunas especificadas, nenhuma linha da tabela tem o mesmo valor. A declaração falha se houver mais de uma linha na tabela com os mesmos valores para todas as colunas especificadas.

    O exemplo de código a seguir mostra uma declaração uniqueKeys no bloco config de uma tabela:

config {
  type: "table",
  assertions: {
    uniqueKeys: [["user_id"], ["signup_date", "customer_id"]]
  }
}
SELECT ...

Adicionar asserções ao bloco config

Para adicionar asserções ao bloco de configuração de uma tabela, siga estas etapas:

  1. No espaço de trabalho de desenvolvimento, no painel Arquivos, selecione um arquivo SQLX de definição de tabela.
  2. No bloco config do arquivo de tabela, insira assertions: {}.
  3. Em assertions: {}, adicione suas declarações.
  4. Opcional: clique em Formatar.

O exemplo de código a seguir mostra as condições adicionadas no bloco config:

config {
  type: "table",
  assertions: {
    uniqueKey: ["user_id"],
    nonNull: ["user_id", "customer_id"],
    rowConditions: [
      'signup_date is null or signup_date > "2019-01-01"',
      'email like "%@%.%"'
    ]
  }
}
SELECT ...

Criar declarações manuais com SQLX

As declarações manuais são consultas SQL escritas em um arquivo SQLX dedicado. Uma consulta SQL de declaração manual precisa retornar zero linhas. Se a consulta retornar linhas quando for executada, a declaração vai falhar.

Para adicionar declarações manuais em um novo arquivo SQLX, siga estas etapas:

  1. No painel Arquivos, ao lado de definitions/, clique no menu Mais.
  2. Selecione Criar arquivo.
  3. No campo Adicionar um caminho de arquivo, insira o nome do arquivo seguido por .sqlx. Por exemplo, definitions/custom_assertion.sqlx.

    Os nomes de arquivo só podem incluir números, letras, hifens e sublinhados.

  4. Selecione Criar arquivo.

  5. No painel Arquivos, clique no novo arquivo.

  6. No arquivo, insira:

    config {
      type: "assertion"
    }
    
  7. Abaixo do bloco config, escreva sua consulta SQL ou várias consultas.

  8. Opcional: clique em Formatar.

O exemplo de código a seguir mostra uma declaração manual em um arquivo SQLX que afirma que os campos A, B e c nunca são NULL em sometable:

config { type: "assertion" }

SELECT
  *
FROM
  ${ref("sometable")}
WHERE
  a IS NULL
  OR b IS NULL
  OR c IS NULL

Testar a qualidade de dados com testes de unidade

Um teste de unidade é um teste de qualidade de dados, definido em um arquivo .sqlx dedicado, que simula todas as dependências da ação do fluxo de trabalho testado e fornece os resultados esperados. É possível usar testes de unidade para testar ações do Dataform com entradas simuladas controladas e verificar se o código de ação processa casos extremos, valores nulos, agregações, expressões regulares e lógica condicional corretamente.

Mocks para dependências de ações, como tabelas predecessoras, visualizações ou declarações brutas referenciadas na função ${ref()}, são definidas em blocos input. Cada bloco input faz referência a uma dependência pelo nome e contém uma consulta SQL que define as linhas simuladas. Essa consulta geralmente é uma série de instruções SELECT combinadas com UNION ALL. Os resultados esperados são consultas SQL que representam os resultados da execução das entradas especificadas na instrução SQL da ação do fluxo de trabalho.

O Dataform executa testes de unidade linha por linha e compara o resultado real da execução da lógica SQL de uma ação de fluxo de trabalho com dados simulados e um conjunto de resultados esperado.

Os testes de unidade são resolvidos nos seguintes estados:

  • SUCCESS: o teste foi aprovado. Os resultados reais correspondem aos esperados.
  • FAILURE: o teste falhou. Os resultados reais não correspondem aos esperados.

Limitações

Os testes de unidade do Dataform estão disponíveis com as seguintes limitações:

  • Os testes de unidade estão disponíveis na versão 3.0.56 e mais recentes do Dataform Core.
  • O tamanho máximo dos dados de entrada em um teste de unidade é de 100 linhas por entrada.

Criar testes de unidade

Armazene arquivos .sqlx para testes de unidade no diretório definitions/. Para criar um novo arquivo de teste de unidade .sqlx no diretório definitions/, siga estas etapas:

  1. No Google Cloud console, acesse a página Dataform.

    Acessar a página do Dataform

  2. Selecione um repositório.

  3. Selecione um espaço de trabalho de desenvolvimento.

  4. No painel Arquivos, ao lado de definitions/, clique no menu Mais.

  5. Selecione Criar arquivo.

  6. No painel Criar novo arquivo, faça o seguinte:

    1. No campo Adicionar um caminho de arquivo, depois de definitions/, insira o nome do arquivo seguido por _test.sqlx. Por exemplo, definitions/customer_spend_test.sqlx.

      Os nomes de arquivo só podem incluir números, letras, hifens e sublinhados.

    2. Selecione Criar arquivo.

  7. No arquivo de teste, adicione o seguinte bloco config:

    config {
      type: "test",
      dataset: "ACTION_NAME"
    }
    

    Substitua ACTION_NAME pelo nome da ação que esse teste valida.

  8. Para simular a ação testada, adicione um bloco input para cada dependência de ação e escreva uma consulta SQL testando essa dependência no seguinte formato:

    input "DEPENDENCY_NAME" {
    SELECT ...
    SELECT ...
    }
    

    Substitua DEPENDENCY_NAME pelo nome da dependência de ação testada que essa entrada simula.

  9. Abaixo dos blocos input, escreva consultas SQL padrão que representam as linhas de saída esperadas no seguinte formato:

    -- Expected Output
    SELECT ...
    SELECT ...
    

As consultas de saída esperadas devem retornar apenas as linhas e colunas que a ação testada deve produzir com base nas entradas simuladas.

O exemplo de código a seguir mostra a ação de fluxo de trabalho customer_spend.sqlx:

config {
type: "table",
name: "customer_spend"
}

SELECT
  c.customer_id,
  c.name,
  SUM(o.amount) AS total_completed_amount
FROM
  ${ref("source_customers")} c
  JOIN
  ${ref("source_orders")} o
  ON c.customer_id = o.customer_id
WHERE
  o.status = 'COMPLETED'
GROUP BY
  1, 2

O exemplo de código a seguir mostra o teste de unidade customer_spend_test.sqlx que simula dependências da ação customer_spend.sqlx e define os resultados esperados para as simulações:

config {
  type: "test",
  dataset: "customer_spend"
}

input "source_customers" {
  SELECT 101 AS customer_id, 'Alice' AS name UNION ALL
  SELECT 102 AS customer_id, 'Bob' AS name UNION ALL
  SELECT 103 AS customer_id, 'Charlie' AS name
}

input "source_orders" {
  -- Alice has one completed and one pending order
  SELECT 1 AS order_id, 101 AS customer_id, 'COMPLETED' AS status, 100.0 AS amount UNION ALL
  SELECT 2 AS order_id, 101 AS customer_id, 'PENDING' AS status, 50.0 AS amount UNION ALL
  -- Bob has one completed order
  SELECT 3 AS order_id, 102 AS customer_id, 'COMPLETED' AS status, 250.0 AS amount UNION ALL
  -- Charlie has no orders
  SELECT 4 AS order_id, 999 AS customer_id, 'COMPLETED' AS status, 10.0 AS amount
}

-- Expected Output
SELECT 101 AS customer_id, 'Alice' AS name, 100.0 AS total_completed_amount UNION ALL
SELECT 102 AS customer_id, 'Bob' AS name, 250.0 AS total_completed_amount

Executar testes unitários

Para executar testes de unidade, siga estas etapas:

Console

  1. No Google Cloud console, acesse a página Dataform.

    Acessar a página do Dataform

  2. Selecione um repositório.

  3. Selecione um espaço de trabalho de desenvolvimento.

  4. Clique em Iniciar execução  > Executar ações.

  5. No painel Executar, na seção Modo de execução, selecione Testes de unidade.

  6. Selecione uma das seguintes opções:

    • Selecionar testes de unidade: executa os testes de unidade que você seleciona manualmente.
    • Selecionar testes de unidade com tags: executa testes de unidade com uma tag selecionada.
    • Todos os testes de unidade: executa todos os testes de unidade no espaço de trabalho.
  7. Opcional: nas seções Opções de execução, marque a caixa de seleção Executar como job interativo com alta prioridade para executar testes de unidade imediatamente, priorizando a velocidade de execução.

    Se você não marcar a caixa de seleção Executar como job interativo com alta prioridade, o Dataform vai executar testes de unidade usando recursos de lote por padrão, priorizando a economia de custos de computação.

  8. Clique em Iniciar execução.

API

Para executar testes de unidade de maneira programática, crie uma invocação de fluxo de trabalho usando o método WorkflowInvocations.create e defina os seguintes parâmetros de execução de teste de unidade no objeto invocationConfig:

"executionMode": "UNIT_TESTS_ONLY"
Esse parâmetro, definido como "UNIT_TESTS_ONLY", aciona a execução dos testes de unidade definidos no repositório.
Opcional: "queryPriority": "INTERACTIVE"
Quando esse parâmetro é definido como "INTERACTIVE", o Dataform executa consultas imediatamente. Se não for definida, o Dataform vai executar testes de unidade com a prioridade de consulta em lote padrão.
Opcional: "includedTargets": []
Esse parâmetro permite especificar testes de unidade para que o Dataform execute apenas esses testes.
Opcional: "includedTags": []
Com esse parâmetro, é possível especificar tags para que o Dataform execute apenas os testes de unidade marcados com elas.

O exemplo de código a seguir mostra o corpo de uma invocação de fluxo de trabalho que executa todos os testes de unidade definidos no repositório my-repo com a prioridade de consulta em lote padrão:

{
  "compilationResult": "projects/my-project/locations/us/repositories/my-repo/compilationResults/my-compilation-id",
  "invocationConfig": {
    "executionMode": "UNIT_TESTS_ONLY"
  }
}

O exemplo de código a seguir mostra o corpo de uma invocação de fluxo de trabalho que executa apenas o teste de unidade my-test com a prioridade de consulta interativa:

{
  "compilationResult": "projects/my-project/locations/us/repositories/my-repo/compilationResults/my-compilation-id",
  "invocationConfig": {
    "executionMode": "UNIT_TESTS_ONLY",
    "queryPriority": "INTERACTIVE",
    "includedTargets": [
      {
        "database": "my-project",
        "schema": "my-dataset",
        "name": "my-test"
      }
    ]
  }
}

O exemplo de código a seguir mostra o corpo de uma invocação de fluxo de trabalho que executa testes de unidade no repositório my-repo marcados com test-tag-1 ou test-tag-2:

{
  "compilationResult": "projects/my-project/locations/us/repositories/my-repo/compilationResults/my-compilation-id",
  "invocationConfig": {
    "executionMode": "UNIT_TESTS_ONLY",
    "queryPriority": "INTERACTIVE",
    "includedTags": [
      "test-tag-1",
      "test-tag-2"
    ]
  }
}

Inspecionar os resultados dos testes de unidade

Você pode inspecionar as diferenças entre os scripts esperados e reais de um teste de unidade no Gráfico compilado ou em Execuções.

Gráfico compilado

Para conferir os scripts real e esperado de um teste de unidade no gráfico compilado de ações do fluxo de trabalho, siga estas etapas:

  1. No Google Cloud console, acesse a página Dataform.

    Acessar a página do Dataform

  2. Selecione um repositório.

  3. Selecione um espaço de trabalho de desenvolvimento.

  4. Opcional: para ver os testes de unidade vinculados às ações que eles testam, em vez de vê-los como nós de gráfico independentes, defina a configuração includeTestsInCompiledGraph como true no arquivo workflow_settings.yaml:

    1. Selecione o arquivo workflow_settings.yaml.
    2. Adicione o seguinte código:
    includeTestsInCompiledGraph: true
    
  5. Clique em Gráfico compilado.

  6. No gráfico compilado, selecione um teste de unidade e clique em Consulta.

  7. Compare o Script SQL real e o Script SQL esperado.

Execuções

  1. No Google Cloud console, acesse a página Dataform.

    Acessar a página do Dataform

  2. Selecione um repositório.

  3. Selecione um espaço de trabalho de desenvolvimento.

  4. Clique em Execuções e em Ver detalhes ao lado do teste de unidade selecionado.

  5. Compare a consulta de resultados reais e a consulta de resultados esperados.

Práticas recomendadas para testes de unidade

Mantenha os conjuntos de dados simulados pequenos
Mantenha os dados de entrada simulados com menos de 10 linhas para uma compilação mais rápida e uma depuração mais fácil.
Especificar uma ordem de linha explícita
Sempre adicione uma cláusula ORDER BY à consulta de ação e à consulta de saída esperada para garantir a ordenação determinística das linhas durante a avaliação.
Converter explicitamente colunas nas instruções simuladas
A conversão explícita de colunas nas instruções de simulação, por exemplo, usando CAST(100 AS INT64), mantém a rigidez de tipo e evita erros de compilação.
Incluir casos de teste com valores NULL ou ausentes
Incluir casos de teste com NULL ou valores ausentes nas consultas de simulação de entrada garante que as instruções COALESCE, as operações de string e os critérios de filtro processem com segurança dados de produção incompletos ou nulos.

O exemplo de código a seguir mostra um caso de teste NULL:

input "source_customers" {
  SELECT 101 AS customer_id, 'Alice' AS name UNION ALL
  SELECT 102 AS customer_id, NULL AS name -- Test null handling
}

A seguir