Bagikan melalui


Uji unit untuk komputer jinjing

Anda dapat menggunakan pengujian unit untuk membantu meningkatkan kualitas dan konsistensi kode notebook Anda. Pengujian unit adalah pendekatan untuk menguji unit kode mandiri, seperti fungsi, awal dan sering. Ini membantu Anda menemukan masalah dengan kode Anda lebih cepat, mengungkap asumsi yang keliru tentang kode Anda lebih cepat, dan menyederhanakan upaya pengodean keseluruhan Anda.

Artikel ini adalah pengantar pengujian unit dasar dengan fungsi. Konsep tingkat lanjut seperti kelas dan antarmuka pengujian unit, serta penggunaan stub, mock, dan kerangka uji, meskipun juga didukung dalam pengujian unit untuk notebook, berada di luar cakupan artikel ini. Artikel ini juga tidak mencakup jenis metode pengujian lain, seperti pengujian integrasi, pengujian sistem, pengujian penerimaan, atau metode pengujian non-fungsi seperti pengujian performa atau pengujian kegunaan.

Artikel ini menunjukkan hal berikut:

  • Cara mengatur fungsi dan pengujian unitnya.
  • Cara menulis fungsi di Python, R, Scala, serta fungsi yang didefinisikan pengguna di SQL, yang dirancang dengan baik untuk pengujian unit.
  • Cara memanggil fungsi-fungsi ini dari notebook Python, R, Scala, dan SQL.
  • Cara menulis pengujian unit di Python, R, dan Scala dengan menggunakan kerangka kerja pengujian populer pytest untuk Python, testthat untuk R, dan ScalaTest untuk Scala. Juga cara menulis SQL untuk menguji unit fungsi yang dibuat pengguna SQL (SQL UDF).
  • Cara menjalankan pengujian unit ini dari notebook Python, R, Scala, dan SQL.

Nota

Azure Databricks merekomendasikan untuk menulis dan menjalankan pengujian unit Anda di buku catatan. Meskipun Anda dapat menjalankan beberapa perintah di terminal web, terminal web memiliki lebih banyak batasan, seperti kurangnya dukungan untuk Spark. Lihat Menjalankan perintah shell di terminal web Azure Databricks.

Mengatur fungsi dan pengujian unit

Ada beberapa pendekatan umum untuk mengatur fungsi Anda dan pengujian unit fungsi tersebut menggunakan notebook. Setiap pendekatan memiliki manfaat dan tantangannya.

Untuk notebook Python, R, dan Scala, pendekatan umum meliputi yang berikut ini:

  • Simpan fungsi dan pengujian unitnya di luar notebook..
    • Manfaat: Anda dapat memanggil fungsi-fungsi ini dengan dan di luar notebook. Kerangka kerja pengujian dirancang lebih baik untuk menjalankan pengujian di luar notebook.
    • Tantangan: Pendekatan ini tidak didukung untuk notebook Scala. Pendekatan ini juga meningkatkan jumlah file yang akan dilacak dan dikelola.
  • Simpan fungsi dalam satu buku catatan dan pengujian unitnya di buku catatan terpisah..
    • Manfaat: Fungsi-fungsi ini lebih mudah digunakan kembali di seluruh notebook.
    • Tantangan: Jumlah notebook yang harus dilacak dan dipelihara semakin bertambah. Fungsi-fungsi ini tidak dapat digunakan di luar notebook. Fungsi-fungsi ini juga bisa lebih sulit diuji di luar notebook.
  • Simpan fungsi dan pengujian unitnya dalam notebook yang sama..
    • Manfaat: Fungsi dan pengujian unitnya disimpan dalam satu notebook untuk pelacakan dan pemeliharaan yang lebih mudah.
    • Tantangan: Fungsi-fungsi ini bisa lebih sulit digunakan kembali di seluruh notebook. Fungsi-fungsi ini tidak dapat digunakan di luar notebook. Fungsi-fungsi ini juga bisa lebih sulit diuji di luar notebook.

Untuk notebook Python dan R, Databricks merekomendasikan penyimpanan fungsi dan pengujian unitnya di luar notebook. Untuk notebook Scala, Databricks merekomendasikan termasuk fungsi dalam satu buku catatan dan pengujian unitnya di buku catatan terpisah.

Untuk notebook SQL, Databricks merekomendasikan agar Anda menyimpan fungsi sebagai fungsi yang ditentukan pengguna SQL (SQL UDF) dalam skema Anda (juga dikenal sebagai database). Anda kemudian dapat memanggil UDF SQL ini dan pengujian unitnya dari notebook SQL.

Tulis fungsi

Bagian ini menjelaskan sekumpulan fungsi contoh sederhana yang menentukan hal berikut:

  • Apakah tabel ada dalam database.
  • Apakah kolom ada dalam tabel.
  • Berapa banyak baris yang ada di kolom untuk nilai dalam kolom tersebut.

Fungsi-fungsi ini dimaksudkan agar sederhana, sehingga Anda dapat fokus pada detail pengujian unit dalam artikel ini daripada fokus pada fungsi itu sendiri.

Untuk mendapatkan hasil pengujian unit terbaik, fungsi harus mengembalikan satu hasil yang dapat diprediksi dan merupakan jenis data tunggal. Misalnya, untuk memeriksa apakah ada sesuatu, fungsi harus mengembalikan nilai boolean true atau false. Untuk mengembalikan jumlah baris yang ada, fungsi ini harus mengembalikan bilangan bulat yang tidak negatif. Seharusnya tidak, dalam contoh pertama, mengembalikan salah jika ada sesuatu yang tidak ada atau hal itu sendiri jika memang ada. Demikian juga, untuk contoh kedua, seharusnya tidak mengembalikan jumlah baris yang ada atau memberikan hasil 'salah' jika tidak ada baris yang ditemukan.

Anda dapat menambahkan fungsi-fungsi ini ke ruang kerja Azure Databricks yang ada sebagai berikut, di Python, R, Scala, atau SQL.

Phyton

Kode berikut mengasumsikan Anda telah Menyiapkan folder Databricks Git, menambahkan repositori, dan membuka repositori di ruang kerja Azure Databricks Anda.

Buat file bernama myfunctions.py dalam repositori, dan tambahkan konten berikut ke file. Contoh lain dalam artikel ini mengharapkan file ini diberi nama myfunctions.py. Anda dapat menggunakan nama yang berbeda untuk file Anda sendiri.

import pyspark
from pyspark.sql import SparkSession
from pyspark.sql.functions import col

# Because this file is not a Databricks notebook, you
# must create a Spark session. Databricks notebooks
# create a Spark session for you by default.
spark = SparkSession.builder \
                    .appName('integrity-tests') \
                    .getOrCreate()

# Does the specified table exist in the specified database?
def tableExists(tableName, dbName):
  return spark.catalog.tableExists(f"{dbName}.{tableName}")

# Does the specified column exist in the given DataFrame?
def columnExists(dataFrame, columnName):
  if columnName in dataFrame.columns:
    return True
  else:
    return False

# How many rows are there for the specified value in the specified column
# in the given DataFrame?
def numRowsInColumnForValue(dataFrame, columnName, columnValue):
  df = dataFrame.filter(col(columnName) == columnValue)

  return df.count()

R

Kode berikut mengasumsikan Anda telah Menyiapkan folder Databricks Git, menambahkan repositori, dan membuka repositori di ruang kerja Azure Databricks Anda.

Buat file bernama myfunctions.r dalam repositori, dan tambahkan konten berikut ke file. Contoh lain dalam artikel ini mengharapkan file ini diberi nama myfunctions.r. Anda dapat menggunakan nama yang berbeda untuk file Anda sendiri.

library(SparkR)

# Does the specified table exist in the specified database?
table_exists <- function(table_name, db_name) {
  tableExists(paste(db_name, ".", table_name, sep = ""))
}

# Does the specified column exist in the given DataFrame?
column_exists <- function(dataframe, column_name) {
  column_name %in% colnames(dataframe)
}

# How many rows are there for the specified value in the specified column
# in the given DataFrame?
num_rows_in_column_for_value <- function(dataframe, column_name, column_value) {
  df = filter(dataframe, dataframe[[column_name]] == column_value)

  count(df)
}

Scala

Buat buku catatan Scala bernama myfunctions dengan konten berikut. Contoh lain dalam artikel ini menganggap buku catatan ini diberi nama myfunctions. Anda bisa menggunakan nama yang berbeda untuk buku catatan Anda sendiri.

import org.apache.spark.sql.DataFrame
import org.apache.spark.sql.functions.col

// Does the specified table exist in the specified database?
def tableExists(tableName: String, dbName: String) : Boolean = {
  return spark.catalog.tableExists(dbName + "." + tableName)
}

// Does the specified column exist in the given DataFrame?
def columnExists(dataFrame: DataFrame, columnName: String) : Boolean = {
  val nameOfColumn = null

  for(nameOfColumn <- dataFrame.columns) {
    if (nameOfColumn == columnName) {
      return true
    }
  }

  return false
}

// How many rows are there for the specified value in the specified column
// in the given DataFrame?
def numRowsInColumnForValue(dataFrame: DataFrame, columnName: String, columnValue: String) : Long = {
  val df = dataFrame.filter(col(columnName) === columnValue)

  return df.count()
}

SQL

Kode berikut mengasumsikan Anda memiliki diamond himpunan data sampel pihak ketiga dalam skema bernama default dalam katalog bernama main yang dapat diakses dari ruang kerja Azure Databricks Anda. Jika katalog atau skema yang ingin Anda gunakan memiliki nama yang berbeda, ubah salah satu atau kedua pernyataan berikut agar USE cocok.

Buat buku catatan SQL dan tambahkan konten berikut ke buku catatan baru ini. Kemudian lampirkan buku catatan ke kluster dan jalankan buku catatan untuk menambahkan SQL UDF berikut ke katalog dan skema yang ditentukan.

Nota

SQL UDF table_exists dan column_exists hanya berfungsi dengan Unity Catalog. Dukungan UDF SQL untuk Katalog Unity ada di Pratinjau Umum.

USE CATALOG main;
USE SCHEMA default;

CREATE OR REPLACE FUNCTION table_exists(catalog_name STRING,
                                        db_name      STRING,
                                        table_name   STRING)
  RETURNS BOOLEAN
  RETURN if(
    (SELECT count(*) FROM system.information_schema.tables
     WHERE table_catalog = table_exists.catalog_name
       AND table_schema  = table_exists.db_name
       AND table_name    = table_exists.table_name) > 0,
    true,
    false
  );

CREATE OR REPLACE FUNCTION column_exists(catalog_name STRING,
                                         db_name      STRING,
                                         table_name   STRING,
                                         column_name  STRING)
  RETURNS BOOLEAN
  RETURN if(
    (SELECT count(*) FROM system.information_schema.columns
     WHERE table_catalog = column_exists.catalog_name
       AND table_schema  = column_exists.db_name
       AND table_name    = column_exists.table_name
       AND column_name   = column_exists.column_name) > 0,
    true,
    false
  );

CREATE OR REPLACE FUNCTION num_rows_for_clarity_in_diamonds(clarity_value STRING)
  RETURNS BIGINT
  RETURN SELECT count(*)
         FROM main.default.diamonds
         WHERE clarity = clarity_value

Fungsi panggilan

Bagian ini menjelaskan kode yang memanggil fungsi sebelumnya. Anda dapat menggunakan fungsi-fungsi ini, misalnya, untuk menghitung jumlah baris dalam tabel di mana nilai tertentu ada dalam kolom yang ditentukan. Namun, Anda ingin memeriksa apakah tabel benar-benar ada, dan apakah kolom benar-benar ada dalam tabel itu, sebelum Anda melanjutkan. Kode berikut memeriksa kondisi ini.

Jika Anda menambahkan fungsi dari bagian sebelumnya ke ruang kerja Azure Databricks, Anda dapat memanggil fungsi-fungsi ini dari ruang kerja Anda sebagai berikut.

Phyton

Buat buku catatan Python di folder yang sama dengan file sebelumnya myfunctions.py di repositori Anda, dan tambahkan konten berikut ke buku catatan. Ubah nilai variabel untuk nama tabel, nama skema (database), nama kolom, dan nilai kolom sesuai kebutuhan. Kemudian lampirkan buku catatan ke kluster dan jalankan buku catatan untuk melihat hasilnya.

from myfunctions import *

tableName   = "diamonds"
dbName      = "default"
columnName  = "clarity"
columnValue = "VVS2"

# If the table exists in the specified database...
if tableExists(tableName, dbName):

  df = spark.sql(f"SELECT * FROM {dbName}.{tableName}")

  # And the specified column exists in that table...
  if columnExists(df, columnName):
    # Then report the number of rows for the specified value in that column.
    numRows = numRowsInColumnForValue(df, columnName, columnValue)

    print(f"There are {numRows} rows in '{tableName}' where '{columnName}' equals '{columnValue}'.")
  else:
    print(f"Column '{columnName}' does not exist in table '{tableName}' in schema (database) '{dbName}'.")
else:
  print(f"Table '{tableName}' does not exist in schema (database) '{dbName}'.") 

R

Buat buku catatan R di folder yang sama dengan file sebelumnya myfunctions.r di repositori Anda, dan tambahkan konten berikut ke buku catatan. Ubah nilai variabel untuk nama tabel, nama skema (database), nama kolom, dan nilai kolom sesuai kebutuhan. Kemudian lampirkan buku catatan ke kluster dan jalankan buku catatan untuk melihat hasilnya.

library(SparkR)
source("myfunctions.r")

table_name   <- "diamonds"
db_name      <- "default"
column_name  <- "clarity"
column_value <- "VVS2"

# If the table exists in the specified database...
if (table_exists(table_name, db_name)) {

  df = sql(paste("SELECT * FROM ", db_name, ".", table_name, sep = ""))

  # And the specified column exists in that table...
  if (column_exists(df, column_name)) {
    # Then report the number of rows for the specified value in that column.
    num_rows = num_rows_in_column_for_value(df, column_name, column_value)

    print(paste("There are ", num_rows, " rows in table '", table_name, "' where '", column_name, "' equals '", column_value, "'.", sep = "")) 
  } else {
    print(paste("Column '", column_name, "' does not exist in table '", table_name, "' in schema (database) '", db_name, "'.", sep = ""))
  }

} else {
  print(paste("Table '", table_name, "' does not exist in schema (database) '", db_name, "'.", sep = ""))
}

Scala

Buat buku catatan Scala lain di folder yang sama dengan buku catatan Scala sebelumnya myfunctions , dan tambahkan konten berikut ke buku catatan baru ini.

Di sel pertama notebook baru ini, tambahkan kode berikut, yang memanggil perintah sihir %run. Sihir ini membuat konten myfunctions buku catatan tersedia untuk buku catatan baru Anda.

%run ./myfunctions

Di sel kedua buku catatan baru ini, tambahkan kode berikut. Ubah nilai variabel untuk nama tabel, nama skema (database), nama kolom, dan nilai kolom sesuai kebutuhan. Kemudian lampirkan buku catatan ke kluster dan jalankan buku catatan untuk melihat hasilnya.

val tableName   = "diamonds"
val dbName      = "default"
val columnName  = "clarity"
val columnValue = "VVS2"

// If the table exists in the specified database...
if (tableExists(tableName, dbName)) {

  val df = spark.sql("SELECT * FROM " + dbName + "." + tableName)

  // And the specified column exists in that table...
  if (columnExists(df, columnName)) {
    // Then report the number of rows for the specified value in that column.
    val numRows = numRowsInColumnForValue(df, columnName, columnValue)

    println("There are " + numRows + " rows in '" + tableName + "' where '" + columnName + "' equals '" + columnValue + "'.")
  } else {
    println("Column '" + columnName + "' does not exist in table '" + tableName + "' in database '" + dbName + "'.")
  }

} else {
  println("Table '" + tableName + "' does not exist in database '" + dbName + "'.")
}

SQL

Tambahkan kode berikut ke sel baru di buku catatan sebelumnya atau ke sel di buku catatan terpisah. Ubah nama skema atau katalog jika perlu untuk mencocokkan milik Anda, lalu jalankan sel ini untuk melihat hasilnya.

SELECT CASE
-- If the table exists in the specified catalog and schema...
WHEN
  table_exists("main", "default", "diamonds")
THEN
  -- And the specified column exists in that table...
  (SELECT CASE
   WHEN
     column_exists("main", "default", "diamonds", "clarity")
   THEN
     -- Then report the number of rows for the specified value in that column.
     printf("There are %d rows in table 'main.default.diamonds' where 'clarity' equals 'VVS2'.",
            num_rows_for_clarity_in_diamonds("VVS2"))
   ELSE
     printf("Column 'clarity' does not exist in table 'main.default.diamonds'.")
   END)
ELSE
  printf("Table 'main.default.diamonds' does not exist.")
END

Menulis pengujian pelajaran

Bagian ini menjelaskan kode yang menguji masing-masing fungsi yang dijelaskan menuju awal artikel ini. Jika Anda membuat perubahan pada fungsi di masa mendatang, Anda dapat menggunakan pengujian unit untuk menentukan apakah fungsi tersebut masih berfungsi seperti yang Anda harapkan.

Jika Anda menambahkan fungsi ke awal artikel ini ke ruang kerja Azure Databricks, Anda dapat menambahkan pengujian unit untuk fungsi-fungsi ini ke ruang kerja Anda sebagai berikut.

Phyton

Buat file lain bernama test_myfunctions.py di folder yang sama dengan file sebelumnya myfunctions.py di repositori Anda, dan tambahkan konten berikut ke file. Secara default, pytest cari .py file yang namanya dimulai dengan test_ (atau diakhir dengan _test) untuk diuji. Demikian pula, secara default, pytest melihat ke dalam file-file ini untuk fungsi yang namanya dimulai dengan test_ untuk menguji.

Secara umum, praktik terbaik adalah untuk tidak menjalankan pengujian unit terhadap fungsi yang bekerja dengan data produksi. Ini sangat penting untuk fungsi yang menambahkan, menghapus, atau mengubah data. Untuk melindungi data produksi agar tidak disusupi oleh pengujian unit dengan cara yang tidak terduga, Anda harus menjalankan pengujian unit terhadap data non-produksi. Salah satu pendekatan umum adalah membuat data palsu yang sedekat mungkin dengan data produksi. Contoh kode berikut membuat data palsu untuk dijalankan oleh pengujian unit.

import pytest
import pyspark
from myfunctions import *
from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StructField, IntegerType, FloatType, StringType

tableName    = "diamonds"
dbName       = "default"
columnName   = "clarity"
columnValue  = "SI2"

# Because this file is not a Databricks notebook, you
# must create a Spark session. Databricks notebooks
# create a Spark session for you by default.
spark = SparkSession.builder \
                    .appName('integrity-tests') \
                    .getOrCreate()

# Create fake data for the unit tests to run against.
# In general, it is a best practice to not run unit tests
# against functions that work with data in production.
schema = StructType([ \
  StructField("_c0",     IntegerType(), True), \
  StructField("carat",   FloatType(),   True), \
  StructField("cut",     StringType(),  True), \
  StructField("color",   StringType(),  True), \
  StructField("clarity", StringType(),  True), \
  StructField("depth",   FloatType(),   True), \
  StructField("table",   IntegerType(), True), \
  StructField("price",   IntegerType(), True), \
  StructField("x",       FloatType(),   True), \
  StructField("y",       FloatType(),   True), \
  StructField("z",       FloatType(),   True), \
])

data = [ (1, 0.23, "Ideal",   "E", "SI2", 61.5, 55, 326, 3.95, 3.98, 2.43 ), \
         (2, 0.21, "Premium", "E", "SI1", 59.8, 61, 326, 3.89, 3.84, 2.31 ) ]

df = spark.createDataFrame(data, schema)

# Does the table exist?
def test_tableExists():
  assert tableExists(tableName, dbName) is True

# Does the column exist?
def test_columnExists():
  assert columnExists(df, columnName) is True

# Is there at least one row for the value in the specified column?
def test_numRowsInColumnForValue():
  assert numRowsInColumnForValue(df, columnName, columnValue) > 0

R

Buat file lain bernama test_myfunctions.r di folder yang sama dengan file sebelumnya myfunctions.r di repositori Anda, dan tambahkan konten berikut ke file. Secara default, testthat mencari file .r yang namanya dimulai dengan test untuk diuji.

Secara umum, praktik terbaik adalah untuk tidak menjalankan pengujian unit terhadap fungsi yang bekerja dengan data produksi. Ini sangat penting untuk fungsi yang menambahkan, menghapus, atau mengubah data. Untuk melindungi data produksi agar tidak disusupi oleh pengujian unit dengan cara yang tidak terduga, Anda harus menjalankan pengujian unit terhadap data non-produksi. Salah satu pendekatan umum adalah membuat data palsu yang sedekat mungkin dengan data produksi. Contoh kode berikut membuat data palsu untuk dijalankan oleh pengujian unit.

library(testthat)
source("myfunctions.r")

table_name   <- "diamonds"
db_name      <- "default"
column_name  <- "clarity"
column_value <- "SI2"

# Create fake data for the unit tests to run against.
# In general, it is a best practice to not run unit tests
# against functions that work with data in production.
schema <- structType(
  structField("_c0",     "integer"),
  structField("carat",   "float"),
  structField("cut",     "string"),
  structField("color",   "string"),
  structField("clarity", "string"),
  structField("depth",   "float"),
  structField("table",   "integer"),
  structField("price",   "integer"),
  structField("x",       "float"),
  structField("y",       "float"),
  structField("z",       "float"))

data <- list(list(as.integer(1), 0.23, "Ideal",   "E", "SI2", 61.5, as.integer(55), as.integer(326), 3.95, 3.98, 2.43),
             list(as.integer(2), 0.21, "Premium", "E", "SI1", 59.8, as.integer(61), as.integer(326), 3.89, 3.84, 2.31))

df <- createDataFrame(data, schema)

# Does the table exist?
test_that ("The table exists.", {
  expect_true(table_exists(table_name, db_name))
})

# Does the column exist?
test_that ("The column exists in the table.", {
  expect_true(column_exists(df, column_name))
})

# Is there at least one row for the value in the specified column?
test_that ("There is at least one row in the query result.", {
  expect_true(num_rows_in_column_for_value(df, column_name, column_value) > 0)
})

Scala

Buat buku catatan Scala lain di folder yang sama dengan buku catatan Scala sebelumnya myfunctions , dan tambahkan konten berikut ke buku catatan baru ini.

Di sel pertama dari buku catatan baru, tambahkan kode berikut yang memanggil perintah magic %run. Sihir ini membuat konten myfunctions buku catatan tersedia untuk buku catatan baru Anda.

%run ./myfunctions

Di sel kedua, tambahkan kode berikut. Kode ini menentukan pengujian unit Anda dan menentukan cara menjalankannya.

Secara umum, praktik terbaik adalah untuk tidak menjalankan pengujian unit terhadap fungsi yang bekerja dengan data produksi. Ini sangat penting untuk fungsi yang menambahkan, menghapus, atau mengubah data. Untuk melindungi data produksi agar tidak disusupi oleh pengujian unit dengan cara yang tidak terduga, Anda harus menjalankan pengujian unit terhadap data non-produksi. Salah satu pendekatan umum adalah membuat data palsu yang sedekat mungkin dengan data produksi. Contoh kode berikut membuat data palsu untuk dijalankan oleh pengujian unit.

import org.scalatest._
import org.apache.spark.sql.types.{StructType, StructField, IntegerType, FloatType, StringType}
import scala.collection.JavaConverters._

class DataTests extends AsyncFunSuite {

  val tableName   = "diamonds"
  val dbName      = "default"
  val columnName  = "clarity"
  val columnValue = "SI2"

  // Create fake data for the unit tests to run against.
  // In general, it is a best practice to not run unit tests
  // against functions that work with data in production.
  val schema = StructType(Array(
                 StructField("_c0",     IntegerType),
                 StructField("carat",   FloatType),
                 StructField("cut",     StringType),
                 StructField("color",   StringType),
                 StructField("clarity", StringType),
                 StructField("depth",   FloatType),
                 StructField("table",   IntegerType),
                 StructField("price",   IntegerType),
                 StructField("x",       FloatType),
                 StructField("y",       FloatType),
                 StructField("z",       FloatType)
               ))

  val data = Seq(
                  Row(1, 0.23, "Ideal",   "E", "SI2", 61.5, 55, 326, 3.95, 3.98, 2.43),
                  Row(2, 0.21, "Premium", "E", "SI1", 59.8, 61, 326, 3.89, 3.84, 2.31)
                ).asJava

  val df = spark.createDataFrame(data, schema)

  // Does the table exist?
  test("The table exists") {
    assert(tableExists(tableName, dbName) == true)
  }

  // Does the column exist?
  test("The column exists") {
    assert(columnExists(df, columnName) == true)
  }

  // Is there at least one row for the value in the specified column?
  test("There is at least one matching row") {
    assert(numRowsInColumnForValue(df, columnName, columnValue) > 0)
  }
}

nocolor.nodurations.nostacks.stats.run(new DataTests)

Nota

Contoh kode ini menggunakan FunSuite gaya pengujian di ScalaTest. Untuk gaya pengujian lain yang tersedia, lihat Memilih gaya pengujian untuk proyek Anda.

SQL

Sebelum Anda menambahkan pengujian unit, Anda harus menyadari bahwa secara umum, ini adalah praktik terbaik untuk tidak menjalankan pengujian unit terhadap fungsi yang bekerja dengan data dalam produksi. Ini sangat penting untuk fungsi yang menambahkan, menghapus, atau mengubah data. Untuk melindungi data produksi agar tidak disusupi oleh pengujian unit dengan cara yang tidak terduga, Anda harus menjalankan pengujian unit terhadap data non-produksi. Salah satu pendekatan umum adalah menjalankan pengujian unit terhadap tampilan alih-alih tabel.

Untuk membuat tampilan, Anda bisa memanggil CREATE VIEW perintah dari sel baru di buku catatan sebelumnya atau buku catatan terpisah. Contoh berikut mengasumsikan bahwa Anda memiliki tabel yang sudah ada bernama diamonds dalam skema (database) bernama default dalam katalog bernama main. Ubah nama ini agar sesuai dengan nama Anda sendiri sesuai kebutuhan, lalu jalankan hanya sel tersebut.

USE CATALOG main;
USE SCHEMA default;

CREATE VIEW view_diamonds AS
SELECT * FROM diamonds;

Setelah Anda membuat tampilan, tambahkan setiap pernyataan berikut SELECT ke sel barunya sendiri di buku catatan sebelumnya atau ke sel barunya sendiri di buku catatan terpisah. Ubah nama agar sesuai dengan nama Anda sendiri sesuai kebutuhan.

SELECT if(table_exists("main", "default", "view_diamonds"),
          printf("PASS: The table 'main.default.view_diamonds' exists."),
          printf("FAIL: The table 'main.default.view_diamonds' does not exist."));

SELECT if(column_exists("main", "default", "view_diamonds", "clarity"),
          printf("PASS: The column 'clarity' exists in the table 'main.default.view_diamonds'."),
          printf("FAIL: The column 'clarity' does not exists in the table 'main.default.view_diamonds'."));

SELECT if(num_rows_for_clarity_in_diamonds("VVS2") > 0,
          printf("PASS: The table 'main.default.view_diamonds' has at least one row where the column 'clarity' equals 'VVS2'."),
          printf("FAIL: The table 'main.default.view_diamonds' does not have at least one row where the column 'clarity' equals 'VVS2'."));

Menjalankan pengujian unit

Bagian ini menjelaskan cara menjalankan pengujian unit yang Anda kodekan di bagian sebelumnya. Saat menjalankan pengujian unit, Anda mendapatkan hasil yang menunjukkan pengujian unit mana yang lulus dan gagal.

Jika Anda menambahkan pengujian unit dari bagian sebelumnya ke ruang kerja Azure Databricks, Anda dapat menjalankan pengujian unit ini dari ruang kerja Anda. Anda dapat menjalankan pengujian unit ini baik secara manual atau sesuai jadwal.

Phyton

Buat buku catatan Python di folder yang sama dengan file sebelumnya test_myfunctions.py di repositori Anda, dan tambahkan konten berikut.

Di sel pertama buku catatan baru, tambahkan kode berikut, lalu jalankan sel, yang memanggil %pip sihir. Sihir ini menginstal pytest.

%pip install pytest

Di sel kedua, tambahkan kode berikut lalu jalankan sel. Hasil menunjukkan pengujian unit mana yang lulus dan gagal.

import pytest
import sys

# Skip writing pyc files on a readonly filesystem.
sys.dont_write_bytecode = True

# Run pytest.
retcode = pytest.main([".", "-v", "-p", "no:cacheprovider"])

# Fail the cell execution if there are any test failures.
assert retcode == 0, "The pytest invocation failed. See the log for details."

R

Buat buku catatan R di folder yang sama dengan file sebelumnya test_myfunctions.r di repositori Anda, dan tambahkan konten berikut.

Di sel pertama, tambahkan kode berikut, lalu jalankan sel, yang memanggil install.packages fungsi. Fungsi ini menginstal testthat.

install.packages("testthat")

Di sel kedua, tambahkan kode berikut, lalu jalankan sel. Hasil menunjukkan pengujian unit mana yang lulus dan gagal.

library(testthat)
source("myfunctions.r")

test_dir(".", reporter = "tap")

Scala

Jalankan sel pertama dan kemudian sel kedua di notebook dari bagian sebelumnya. Hasil menunjukkan pengujian unit mana yang lulus dan gagal.

SQL

Jalankan masing-masing dari tiga sel di buku catatan dari bagian sebelumnya. Hasil menunjukkan apakah setiap pengujian unit lulus atau gagal.

Jika Anda tidak lagi memerlukan tampilan setelah menjalankan pengujian unit, Anda dapat menghapus tampilan. Untuk menghapus tampilan ini, Anda bisa menambahkan kode berikut ke sel baru dalam salah satu buku catatan sebelumnya lalu hanya menjalankan sel tersebut.

DROP VIEW view_diamonds;

Petunjuk

Anda dapat melihat hasil eksekusi notebook Anda (termasuk hasil pengujian unit) di log driver kluster Anda. Anda juga dapat menentukan lokasi untuk pengiriman log kluster Anda.

Anda dapat menyiapkan integrasi berkelanjutan dan sistem pengiriman atau penyebaran berkelanjutan (CI/CD), seperti GitHub Actions, untuk menjalankan pengujian unit Anda secara otomatis setiap kali kode Anda berubah. Misalnya, lihat cakupan Tindakan GitHub dalam Praktik terbaik rekayasa perangkat lunak untuk notebook.

Sumber daya tambahan

pytest

ujiitu

ScalaTest

SQL