בדיקת איכות הנתונים

במסמך הזה מוסבר איך לבדוק את קוד תהליך העבודה באמצעות טענות לגבי טבלאות וטסטים של יחידות ב-Dataform.

לפני שמתחילים

  1. נכנסים לדף Dataform במסוף Google Cloud .

    מעבר לדף Dataform

  2. בוחרים מאגר או יוצרים מאגר.

  3. בוחרים או יוצרים סביבת עבודה לפיתוח.

  4. יצירת טבלה

התפקידים הנדרשים

כדי לקבל את ההרשאות שדרושות ליצירת טענות נכוֹנוּת (assertion) ובדיקות יחידה (unit testing), צריך לבקש מהאדמין להקצות לכם את תפקידי ה-IAM הבאים:

  • Dataform Editor (roles/dataform.editor) בסביבת העבודה
  • כדי לסנכרן את המטא-נתונים של ההצהרה עם Knowledge Catalog: Dataplex Catalog Editor (roles/dataplex.catalogEditor) בפרויקט או בקבוצת הרשומות @bigquery

להסבר על מתן תפקידים, ראו איך מנהלים את הגישה ברמת הפרויקט, התיקייה והארגון.

יכול להיות שאפשר לקבל את ההרשאות הנדרשות גם באמצעות תפקידים בהתאמה אישית או תפקידים מוגדרים מראש.

בדיקת נתונים באמצעות הצהרות

טענה היא שאילתת בדיקה של איכות הנתונים שמאתרת שורות שמפירות תנאי אחד או יותר שצוינו בשאילתה. אם השאילתה מחזירה שורות כלשהן, הטענה נכשלת. ‫Dataform מריץ הצהרות בכל פעם שהוא מעדכן את תהליך העבודה, והוא מתריע אם הצהרה כלשהי נכשלת.

‫Dataform יוצר באופן אוטומטי תצוגות ב-BigQuery שמכילות את התוצאות של שאילתות טענות שעברו קומפילציה. כפי שהוגדר בקובץ הגדרות תהליך העבודה,‏ Dataform יוצר את התצוגות האלה בסכימת טענות, שבה אפשר לבדוק את תוצאות הטענות.

לדוגמה, עבור סכימת ברירת המחדל dataform_assertions, ‏ Dataform יוצר תצוגה ב-BigQuery בפורמט הבא: dataform_assertions.assertion_name.

אפשר ליצור הצהרות לכל סוגי הטבלאות ב-Dataform: טבלאות, טבלאות מצטברות, תצוגות (views) ותצוגות חומריות (materialized views).

אפשר ליצור הצהרות בדרכים הבאות:

יצירת טענות מובנות

אפשר להוסיף הצהרות מובנות של Dataform לבלוק config של טבלה. ‫Dataform מריץ את ההצהרות האלה אחרי יצירת הטבלה. אחרי ש-Dataform יוצר את הטבלה, אפשר לראות אם האימות עבר בהצלחה בכרטיסייה Workflow execution logs (יומני ביצוע של תהליך העבודה) בסביבת העבודה.

אפשר ליצור את הטענות הבאות בבלוק config של טבלה:

  • nonNull

    התנאי הזה קובע שהעמודות שצוינו לא יכולות להיות ריקות בכל השורות בטבלה. התנאי הזה משמש לעמודות שלעולם לא יכולות להיות null.

    בדוגמת הקוד הבאה מוצגת טענת nonNull בבלוק config של טבלה:

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

    התנאי הזה קובע שכל השורות בטבלה פועלות לפי הלוגיקה המותאמת אישית שהגדרתם. כל תנאי שורה הוא ביטוי SQL מותאם אישית, וכל שורה בטבלה מוערכת לפי כל תנאי שורה. הטענה נכשלת אם שורה כלשהי בטבלה מחזירה את הערך false.

    בדוגמת קוד הבאה מוצגת טענת נכוֹנוּת (assertion) rowConditions בהתאמה אישית בבלוק config של טבלה מצטברת:

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

    התנאי הזה קובע שבעמודה שצוינה, לאף שורה בטבלה אין את אותו הערך.

    בדוגמה הבאה של קוד אפשר לראות טענת uniqueKey בבלוק config של תצוגה:

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

    התנאי הזה קובע שבשום שורה בטבלה לא מופיע אותו ערך בעמודות שצוינו. האימות נכשל אם יש יותר משורה אחת בטבלה עם אותם ערכים בכל העמודות שצוינו.

    בדוגמת הקוד הבאה מוצגת טענת uniqueKeys בבלוק config של טבלה:

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

הוספת טענות נכוֹנוּת (assertions) לבלוק config

כדי להוסיף הצהרות לבלוק ההגדרות של טבלה, פועלים לפי השלבים הבאים:

  1. בסביבת העבודה לפיתוח, בחלונית Files (קבצים), בוחרים קובץ SQLX של הגדרת טבלה.
  2. בבלוק config של קובץ הטבלה, מזינים assertions: {}.
  3. בתוך assertions: {}, מוסיפים את הטענות.
  4. אופציונלי: לוחצים על עיצוב.

בדוגמת הקוד הבאה אפשר לראות את התנאים שנוספו בבלוק 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 ...

יצירת הצהרות ידניות באמצעות SQLX

טענות ידניות הן שאילתות SQL שכותבים בקובץ SQLX ייעודי. שאילתת SQL של טענת נכונות ידנית צריכה להחזיר אפס שורות. אם השאילתה מחזירה שורות כשהיא מופעלת, הטענה נכשלת.

כדי להוסיף הצהרות ידניות בקובץ SQLX חדש, פועלים לפי השלבים הבאים:

  1. בחלונית קבצים, לצד definitions/, לוחצים על סמל האפשרויות הנוספות .
  2. לוחצים על יצירת קובץ.
  3. בשדה Add a file path (הוספת נתיב קובץ), מזינים את שם הקובץ ואחריו את התו .sqlx. לדוגמה, definitions/custom_assertion.sqlx.

    שמות הקבצים יכולים לכלול רק מספרים, אותיות, מקפים וקווים תחתונים.

  4. לוחצים על יצירת קובץ.

  5. בחלונית קבצים, לוחצים על הקובץ החדש.

  6. בקובץ, מזינים את הפרטים הבאים:

    config {
      type: "assertion"
    }
    
  7. מתחת לבלוק config, כותבים את שאילתת ה-SQL או כמה שאילתות.

  8. אופציונלי: לוחצים על עיצוב.

בדוגמת הקוד הבאה מוצגת טענה ידנית בקובץ SQLX, שקובעת שהשדות A, ‏ B ו-c אף פעם לא יהיו NULL ב-sometable:

config { type: "assertion" }

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

בדיקת איכות הנתונים באמצעות בדיקות יחידה

בדיקת יחידה היא בדיקה של איכות הנתונים, שמוגדרת בקובץ .sqlx ייעודי, שמדמה את כל התלות של פעולת זרימת העבודה שנבדקת ומספקת תוצאות צפויות. אפשר להשתמש בבדיקות יחידה כדי לבדוק פעולות ב-Dataform מול קלט מדומה מבוקר, כדי לוודא שקוד הפעולה מטפל בצורה נכונה במקרים קיצוניים, בערכי null, בצבירות, בביטויים רגולריים ובלוגיקה מותנית.

הגדרת מוקאפים לתלות בפעולות, כמו טבלאות קודמות, תצוגות או הצהרות גולמיות שמפנות לפונקציה ${ref()}, מתבצעת בבלוקים של input. כל בלוק input מפנה לתלות לפי השם שלה ומכיל שאילתת SQL שמגדירה את השורות המדומות. השאילתה הזו היא בדרך כלל סדרה של הצהרות SELECT בשילוב עם UNION ALL. התוצאות הצפויות הן שאילתות SQL שמייצגות את התוצאות של הרצת הקלט שצוין על הצהרת ה-SQL של פעולת תהליך העבודה.

ב-Dataform, בדיקות היחידה מופעלות שורה אחר שורה, והתוצאה בפועל של הפעלת לוגיקת ה-SQL של פעולת זרימת עבודה מול נתוני דמה מושווית לקבוצת תוצאות צפויה.

בדיקות יחידה יכולות להיות באחד מהמצבים הבאים:

  • SUCCESS: הבדיקה עברה. התוצאות בפועל תואמות לתוצאות הצפויות.
  • FAILURE: הבדיקה נכשלה. התוצאות בפועל לא תואמות לתוצאות הצפויות.

מגבלות

בדיקות יחידה ב-Dataform זמינות עם המגבלות הבאות:

  • ב-Dataform core בגרסה 3.0.56 ואילך יש בדיקות יחידה.
  • הגודל המקסימלי של נתוני הקלט בבדיקת יחידה הוא 100 שורות לכל קלט.

יצירת בדיקות יחידה

מאחסנים קובצי .sqlx לבדיקות יחידה בספרייה definitions/. כדי ליצור קובץ חדש של בדיקת יחידה .sqlx בספרייה definitions/, פועלים לפי השלבים הבאים:

  1. נכנסים לדף Dataform במסוף Google Cloud .

    מעבר לדף Dataform

  2. בוחרים מאגר.

  3. בוחרים סביבת עבודה לפיתוח.

  4. בחלונית קבצים, לצד definitions/, לוחצים על תפריט האפשרויות הנוספות.

  5. לוחצים על יצירת קובץ.

  6. בחלונית Create new file:

    1. בשדה הוספת נתיב קובץ, אחרי definitions/, מזינים את שם הקובץ ואחריו _test.sqlx. לדוגמה, definitions/customer_spend_test.sqlx.

      שמות הקבצים יכולים לכלול רק מספרים, אותיות, מקפים וקווים תחתונים.

    2. לוחצים על יצירת קובץ.

  7. בקובץ הבדיקה, מוסיפים את הבלוק config הבא:

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

    מחליפים את ACTION_NAME בשם הפעולה שהבדיקה הזו מאמתת.

  8. כדי ליצור מוקאפ של הפעולה שנבדקת, מוסיפים בלוק input לכל תלות בפעולה, וכותבים שאילתת SQL לבדיקת התלות הזו בפורמט הבא:

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

    מחליפים את DEPENDENCY_NAME בשם של התלות בפעולה שנבדקה, שהקלט הזה מדמה.

  9. מתחת לבלוקים input, כותבים שאילתות SQL סטנדרטיות שמייצגות את שורות הפלט הצפויות בפורמט הבא:

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

השאילתות של הפלט הצפוי צריכות להחזיר רק את השורות והעמודות שהפעולה שנבדקת אמורה להפיק בהינתן קלט מדומה.

בדוגמת הקוד הבאה מוצגת פעולת זרימת העבודה 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

בדוגמת הקוד הבאה מוצגת בדיקת יחידה customer_spend_test.sqlx שמבצעת מוקינג לתלות של פעולת customer_spend.sqlx, ומגדירה תוצאות צפויות למוקים:

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

הרצת בדיקות יחידה

כדי להריץ בדיקות יחידה, פועלים לפי השלבים הבאים:

המסוף

  1. נכנסים לדף Dataform במסוף Google Cloud .

    מעבר לדף Dataform

  2. בוחרים מאגר.

  3. בוחרים סביבת עבודה לפיתוח.

  4. לוחצים על התחלת ההפעלה  > ביצוע פעולות.

  5. בחלונית Execute, בקטע Execution mode, בוחרים באפשרות Unit tests.

  6. בוחרים באחת מהאפשרויות הבאות:

    • בחירת בדיקות יחידה: מריץ בדיקות יחידה שאתם בוחרים באופן ידני.
    • בחירת בדיקות יחידה עם תגים: מריץ בדיקות יחידה עם תג נבחר.
    • All unit tests (כל בדיקות היחידה): מריץ את כל בדיקות היחידה בסביבת העבודה.
  7. אופציונלי: בקטע Execution options, מסמנים את התיבה Execute as interactive job with high priority כדי להריץ בדיקות יחידה באופן מיידי, עם עדיפות למהירות הביצוע.

    אם לא מסמנים את תיבת הסימון Execute as interactive job with high priority (הפעלה כמשימה אינטראקקטיבית עם עדיפות גבוהה), Dataform מפעיל בדיקות יחידה באמצעות משאבי אצווה כברירת מחדל, ונותן עדיפות לחיסכון בעלויות מחשוב.

  8. לוחצים על התחלת ההפעלה.

API

כדי להריץ בדיקות יחידה באופן פרוגרמטי, יוצרים הפעלה של זרימת עבודה באמצעות השיטה WorkflowInvocations.create, ומגדירים את פרמטרים ההרצה של בדיקות היחידה באובייקט invocationConfig:

"executionMode": "UNIT_TESTS_ONLY"
הפרמטר הזה, שמוגדר לערך "UNIT_TESTS_ONLY", מפעיל את ההרצה של בדיקות היחידה שמוגדרות במאגר.
"queryPriority": "INTERACTIVE" (אופציונלי)
כשהפרמטר הזה מוגדר לערך "INTERACTIVE",‏ Dataform מריץ שאילתות באופן מיידי. אם לא מוגדרת עדיפות, Dataform מריץ בדיקות יחידה עם עדיפות ברירת המחדל של שאילתת אצווה.
"includedTargets": [] (אופציונלי)
הפרמטר הזה מאפשר לציין בדיקות יחידה כדי שמערכת Dataform תריץ רק את הבדיקות האלה.
"includedTags": [] (אופציונלי)
הפרמטר הזה מאפשר לציין תגים כדי שמערכת Dataform תריץ רק את בדיקות היחידה שתויגו בתגים האלה.

בדוגמת הקוד הבאה מוצג גוף של הפעלת תהליך עבודה שמריץ את כל בדיקות היחידה שמוגדרות במאגר my-repo עם העדיפות שמוגדרת כברירת מחדל לשאילתת אצווה:

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

בדוגמת הקוד הבאה מוצג גוף של הפעלת תהליך עבודה שמריץ רק את בדיקת היחידה my-test עם עדיפות של שאילתה אינטראקטיבית:

{
  "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"
      }
    ]
  }
}

בדוגמת הקוד הבאה מוצג גוף של הפעלת תהליך עבודה שמריץ בדיקות יחידה במאגר my-repo שתויגו בתווית test-tag-1 או 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"
    ]
  }
}

בדיקת תוצאות של בדיקות יחידה

אפשר לבדוק את ההבדלים בין הסקריפטים הצפויים והסקריפטים בפועל של בדיקת יחידה בתרשים המהדר או בהפעלות.

תרשים מסוכם

כדי לראות את התסריטים בפועל ואת התסריטים הצפויים של בדיקת יחידה בתרשים המהודר של פעולות בתהליך העבודה, פועלים לפי השלבים הבאים:

  1. נכנסים לדף Dataform במסוף Google Cloud .

    מעבר לדף Dataform

  2. בוחרים מאגר.

  3. בוחרים סביבת עבודה לפיתוח.

  4. אופציונלי: כדי לראות את בדיקות היחידה שמקושרות לפעולות שהן בודקות, במקום לראות אותן כצמתים עצמאיים בתרשים, מגדירים את ההגדרה includeTestsInCompiledGraph ל-true בקובץ workflow_settings.yaml:

    1. בוחרים את הקובץ workflow_settings.yaml.
    2. מוסיפים את הקוד הבא:
    includeTestsInCompiledGraph: true
    
  5. לוחצים על גרף משולב.

  6. בתרשים המורכב, בוחרים בדיקת יחידה ולוחצים על שאילתה.

  7. משווים בין Actual SQL Script (סקריפט ה-SQL בפועל) לבין Expected SQL Script (סקריפט ה-SQL הצפוי).

הפעלות

  1. נכנסים לדף Dataform במסוף Google Cloud .

    מעבר לדף Dataform

  2. בוחרים מאגר.

  3. בוחרים סביבת עבודה לפיתוח.

  4. לוחצים על Executions (הרצות) ואז על View details (הצגת פרטים) ליד בדיקת היחידה שנבחרה.

  5. משווים בין השאילתה של התוצאות בפועל לבין השאילתה של התוצאות הצפויות.

שיטות מומלצות לבדיקות יחידה

שמירה על גודל קטן של מערכי נתונים לדוגמה
כדי להאיץ את ההידור ולפשט את ניפוי הבאגים, כדאי להגביל את נתוני הקלט המדומים ל-10 שורות.
ציון סדר שורות מפורש
כדי להבטיח סדר שורות דטרמיניסטי במהלך ההערכה, תמיד מוסיפים פסקה של ORDER BY לשאילתת הפעולה ולשאילתת הפלט הצפוי.
המרת עמודות באופן מפורש בהצהרות המדומות
המרת סוגי עמודות באופן מפורש בהצהרות המדומות – לדוגמה, באמצעות CAST(100 AS INT64) – שומרת על הקפדה על סוגים ומונעת שגיאות קומפילציה.
הכללה של תרחישי בדיקה עם NULL או ערכים חסרים
הכללת תרחישי בדיקה עם NULL או ערכים חסרים בשאילתות המוקדמות של הקלט מבטיחה שהצהרות COALESCE, פעולות על מחרוזות וקריטריוני סינון יטפלו בצורה בטוחה בנתוני ייצור לא מלאים או בנתוני ייצור מסוג null.

בדוגמת הקוד הבאה מוצג מקרה בדיקה של 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
}

המאמרים הבאים