Aracılığıyla paylaş


Not defterleri için birim testi

Not defterlerinizin kodunun kalitesini ve tutarlılığını artırmaya yardımcı olması için birim testini kullanabilirsiniz. Birim testi, işlevler gibi bağımsız kod birimlerini erken ve sık test etmeye yönelik bir yaklaşımdır. Bu, kodunuzla ilgili sorunları daha hızlı bulmanıza, kodunuzla ilgili yanlış varsayımları daha erken ortaya çıkarmanıza ve genel kodlama çalışmalarınızı kolaylaştırmanıza yardımcı olur.

Bu makale, işlevlerle temel birim testine giriş niteliğindedir. Birim testi sınıfları ve arabirimleri gibi gelişmiş kavramların yanı sıra saplamalar, sahteler ve test koşumlarının kullanımı ve not defterleri için birim testi yaparken de desteklenmesi bu makalenin kapsamı dışındadır. Bu makale tümleştirme testi, sistem testi, kabul testi veya performans testi veya kullanılabilirlik testi gibi işlevsel olmayan test yöntemleri gibi diğer test yöntemlerini de kapsamaz.

Bu makalede aşağıdakiler gösterilmektedir:

  • İşlevleri ve bunların birim testlerini düzenleme.
  • Python, R, Scala'daki işlevlerin yanı sıra SQL'de birim testi için iyi tasarlanmış kullanıcı tanımlı işlevler yazma.
  • Python, R, Scala ve SQL not defterlerinden bu işlevleri çağırma.
  • Python için pytest, R için test ve Scala için Scala için ScalaTest gibi popüler test çerçevelerini kullanarak Python, R ve Scala'da birim testleri yazma. Ayrıca, sql kullanıcı tanımlı işlevleri (SQL UDF'leri) test eden bir ünitede SQL yazma.
  • Python, R, Scala ve SQL not defterlerinden bu birim testlerini çalıştırma.

İşlevleri ve birim testlerini düzenleme

İşlevlerinizi ve bunların birim testlerini not defterleriyle düzenlemek için birkaç yaygın yaklaşım vardır. Her yaklaşımın avantajları ve zorlukları vardır.

Python, R ve Scala not defterleri için yaygın yaklaşımlar şunlardır:

  • İşlevleri ve birim testlerini not defterleri dışında depolayın..
    • Avantajlar: Bu işlevleri not defterleriyle ve not defterleri dışından çağırabilirsiniz. Test çerçeveleri, testleri not defterleri dışında çalıştıracak şekilde daha iyi tasarlanmıştır.
    • Zorluklar: Bu yaklaşım Scala not defterleri için desteklenmez. Bu yaklaşım, izlenip korunacak dosya sayısını da artırır.
  • İşlevleri tek bir not defterinde ve birim testlerini ayrı bir not defterinde depolayın..
    • Avantajlar: Bu işlevlerin not defterleri arasında yeniden kullanılması daha kolaydır.
    • Zorluklar: İzlenip korunacak not defterlerinin sayısı artar. Bu işlevler not defterlerinin dışında kullanılamaz. Bu işlevlerin not defterleri dışında test edilmesi de daha zor olabilir.
  • İşlevleri ve birim testlerini aynı not defteri içinde depolayın..
    • Avantajlar: İşlevler ve birim testleri, daha kolay izleme ve bakım için tek bir not defterinde depolanır.
    • Zorluklar: Bu işlevlerin not defterleri arasında yeniden kullanılması daha zor olabilir. Bu işlevler not defterlerinin dışında kullanılamaz. Bu işlevlerin not defterleri dışında test edilmesi de daha zor olabilir.

Databricks, Python ve R not defterleri için işlevlerin ve birim testlerinin not defterleri dışında depolanmasını önerir. Scala not defterleri için Databricks, tek bir not defterindeki işlevleri ve bunların birim testlerini ayrı bir not defterine eklemenizi önerir.

Databricks, SQL not defterleri için işlevleri şemalarınızda (veritabanları olarak da bilinir) SQL kullanıcı tanımlı işlevler (SQL UDF'leri) olarak depolamanızı önerir. Daha sonra bu SQL UDF'lerini ve bunların birim testlerini SQL not defterlerinden çağırabilirsiniz.

Yazma işlevleri

Bu bölümde, aşağıdakileri belirleyen basit bir örnek işlev kümesi açıklanmaktadır:

  • Veritabanında tablo bulunup bulunmadığı.
  • Tabloda bir sütunun bulunup bulunmadığı.
  • Bu sütundaki bir değer için bir sütunda kaç satır var?

Bu işlevlerin basit olması amaçlanmıştır, böylece işlevlere odaklanmak yerine bu makaledeki birim testi ayrıntılarına odaklanabilirsiniz.

En iyi birim testi sonuçlarını almak için işlevin tek bir tahmin edilebilir sonuç döndürmesi ve tek bir veri türünde olması gerekir. Örneğin, bir şeyin var olup olmadığını denetlemek için işlevin true veya false boole değeri döndürmesi gerekir. Var olan satır sayısını döndürmek için işlevin negatif olmayan bir tamsayı döndürmesi gerekir. İlk örnekte, bir şey yoksa false veya varsa o şeyin kendisi döndürmemelidir. Benzer şekilde, ikinci örnekte de var olan satır sayısını veya satır yoksa false değerini döndürmemelidir.

Bu işlevleri python, R, Scala veya SQL'de mevcut bir Azure Databricks çalışma alanına aşağıdaki gibi ekleyebilirsiniz.

Python

Aşağıdaki kodda Databricks Git klasörlerini (Repos) ayarladığınız, bir depo eklediğiniz ve deponun Azure Databricks çalışma alanınızda açık olduğu varsayılır.

Depo içinde adlı myfunctions.py bir dosya oluşturun ve dosyaya aşağıdaki içeriği ekleyin. Bu makaledeki diğer örneklerde bu dosyanın olarak adlandırılması myfunctions.pybeklenmiştir. Kendi dosyalarınız için farklı adlar kullanabilirsiniz.

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

Aşağıdaki kodda Databricks Git klasörlerini (Repos) ayarladığınız, bir depo eklediğiniz ve deponun Azure Databricks çalışma alanınızda açık olduğu varsayılır.

Depo içinde adlı myfunctions.r bir dosya oluşturun ve dosyaya aşağıdaki içeriği ekleyin. Bu makaledeki diğer örneklerde bu dosyanın olarak adlandırılması myfunctions.rbeklenmiştir. Kendi dosyalarınız için farklı adlar kullanabilirsiniz.

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

Aşağıdaki içeriklere sahip adlı myfunctions bir Scala not defteri oluşturun. Bu makaledeki diğer örnekler, bu not defterinin olarak adlandırılması myfunctionsbeklenebilir. Kendi not defterleriniz için farklı adlar kullanabilirsiniz.

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

Aşağıdaki kodda, Azure Databricks çalışma alanınızdan erişilebilen adlı bir katalogda adlı maindefault şemada üçüncü taraf örnek veri kümesi elmasları olduğu varsayılır. Kullanmak istediğiniz kataloğun veya şemanın adı farklıysa, aşağıdaki USE deyimlerden birini veya her ikisini de eşleşecek şekilde değiştirin.

Bir SQL not defteri oluşturun ve bu yeni not defterine aşağıdaki içeriği ekleyin. Ardından not defterini bir kümeye ekleyin ve not defterini çalıştırarak belirtilen kataloğa ve şemaya aşağıdaki SQL UDF'lerini ekleyin.

Not

SQL UDF'leri table_exists ve column_exists yalnızca Unity Kataloğu ile çalışır. Unity Kataloğu için SQL UDF desteği Genel Önizleme aşamasındadır.

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

Çağrı işlevleri

Bu bölümde, önceki işlevleri çağıran kod açıklanmaktadır. Bu işlevleri, örneğin, belirtilen değerin belirli bir sütun içinde bulunduğu tablodaki satır sayısını saymak için kullanabilirsiniz. Ancak, devam etmeden önce tablonun gerçekten var olup olmadığını ve sütunun bu tabloda gerçekten var olup olmadığını denetlemek istersiniz. Aşağıdaki kod bu koşulları denetler.

Önceki bölümde yer alan işlevleri Azure Databricks çalışma alanınıza eklediyseniz, bu işlevleri çalışma alanınızdan aşağıdaki gibi çağırabilirsiniz.

Python

Deponuzdaki önceki myfunctions.py dosyayla aynı klasörde bir Python not defteri oluşturun ve not defterine aşağıdaki içeriği ekleyin. Tablo adı, şema (veritabanı) adı, sütun adı ve sütun değeri için değişken değerlerini gerektiği gibi değiştirin. Ardından not defterini bir kümeye ekleyin ve sonuçları görmek için not defterini çalıştırın .

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

Deponuzdaki önceki myfunctions.r dosyayla aynı klasörde bir R not defteri oluşturun ve not defterine aşağıdaki içeriği ekleyin. Tablo adı, şema (veritabanı) adı, sütun adı ve sütun değeri için değişken değerlerini gerektiği gibi değiştirin. Ardından not defterini bir kümeye ekleyin ve sonuçları görmek için not defterini çalıştırın .

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

Önceki myfunctions Scala not defteriyle aynı klasörde başka bir Scala not defteri oluşturun ve bu yeni not defterine aşağıdaki içeriği ekleyin.

Bu yeni not defterinin ilk hücresine , %run magic öğesini çağıran aşağıdaki kodu ekleyin. Bu sihir, not defterinin myfunctions içeriğini yeni not defteriniz için kullanılabilir hale getirir.

%run ./myfunctions

Bu yeni not defterinin ikinci hücresine aşağıdaki kodu ekleyin. Tablo adı, şema (veritabanı) adı, sütun adı ve sütun değeri için değişken değerlerini gerektiği gibi değiştirin. Ardından not defterini bir kümeye ekleyin ve sonuçları görmek için not defterini çalıştırın .

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

Aşağıdaki kodu önceki not defterindeki yeni bir hücreye veya ayrı bir not defterindeki hücreye ekleyin. Gerekirse şema veya katalog adlarını sizinkiyle eşleşecek şekilde değiştirin ve sonuçları görmek için bu hücreyi çalıştırın.

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

Birim testleri yazma

Bu bölümde, bu makalenin başına doğru açıklanan işlevlerin her birini test eden kod açıklanmaktadır. İleride işlevlerde herhangi bir değişiklik yaparsanız, bu işlevlerin hala beklediğiniz gibi çalışıp çalışmadığını belirlemek için birim testlerini kullanabilirsiniz.

İşlevleri bu makalenin başına doğru Azure Databricks çalışma alanınıza eklediyseniz, bu işlevler için birim testlerini çalışma alanınıza aşağıdaki gibi ekleyebilirsiniz.

Python

Deponuzdaki önceki myfunctions.py dosyayla aynı klasörde adlı test_myfunctions.py başka bir dosya oluşturun ve dosyaya aşağıdaki içeriği ekleyin. Varsayılan olarak, pytest adları test etmek için ile başlayan test_ (veya ile _testbiten) dosyaları arar.py. Benzer şekilde, varsayılan olarak, pytest adları test için ile test_ başlayan işlevler için bu dosyaların içine bakar.

Genel olarak, üretimdeki verilerle çalışan işlevlere karşı birim testleri çalıştırmamak en iyi yöntemdir. Bu özellikle verileri ekleyen, kaldıran veya başka bir şekilde değiştiren işlevler için önemlidir. Üretim verilerinizin birim testleriniz tarafından beklenmeyen yollarla tehlikeye atılmasını korumak için üretim dışı verilere karşı birim testleri çalıştırmanız gerekir. Yaygın yaklaşımlardan biri, üretim verilerine mümkün olduğunca yakın sahte veriler oluşturmaktır. Aşağıdaki kod örneği, birim testlerinin çalıştırılması için sahte veriler oluşturur.

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

Deponuzdaki önceki myfunctions.r dosyayla aynı klasörde adlı test_myfunctions.r başka bir dosya oluşturun ve dosyaya aşağıdaki içeriği ekleyin. Varsayılan olarak, testthat adları test için ile test başlayan dosyaları arar.r.

Genel olarak, üretimdeki verilerle çalışan işlevlere karşı birim testleri çalıştırmamak en iyi yöntemdir. Bu özellikle verileri ekleyen, kaldıran veya başka bir şekilde değiştiren işlevler için önemlidir. Üretim verilerinizin birim testleriniz tarafından beklenmeyen yollarla tehlikeye atılmasını korumak için üretim dışı verilere karşı birim testleri çalıştırmanız gerekir. Yaygın yaklaşımlardan biri, üretim verilerine mümkün olduğunca yakın sahte veriler oluşturmaktır. Aşağıdaki kod örneği, birim testlerinin çalıştırılması için sahte veriler oluşturur.

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

Önceki myfunctions Scala not defteriyle aynı klasörde başka bir Scala not defteri oluşturun ve bu yeni not defterine aşağıdaki içeriği ekleyin.

Yeni not defterinin ilk hücresine, sihri çağıran %run aşağıdaki kodu ekleyin. Bu sihir, not defterinin myfunctions içeriğini yeni not defteriniz için kullanılabilir hale getirir.

%run ./myfunctions

İkinci hücreye aşağıdaki kodu ekleyin. Bu kod, birim testlerinizi tanımlar ve bunların nasıl çalıştırılacaklarını belirtir.

Genel olarak, üretimdeki verilerle çalışan işlevlere karşı birim testleri çalıştırmamak en iyi yöntemdir. Bu özellikle verileri ekleyen, kaldıran veya başka bir şekilde değiştiren işlevler için önemlidir. Üretim verilerinizin birim testleriniz tarafından beklenmeyen yollarla tehlikeye atılmasını korumak için üretim dışı verilere karşı birim testleri çalıştırmanız gerekir. Yaygın yaklaşımlardan biri, üretim verilerine mümkün olduğunca yakın sahte veriler oluşturmaktır. Aşağıdaki kod örneği, birim testlerinin çalıştırılması için sahte veriler oluşturur.

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)

Not

Bu kod örneği ScalaTest'te test stilini kullanır FunSuite . Diğer kullanılabilir test stilleri için bkz . Projeniz için test stillerini seçme.

SQL

Birim testleri eklemeden önce, genel olarak üretimdeki verilerle çalışan işlevlere karşı birim testleri çalıştırmamak en iyi yöntemdir. Bu özellikle verileri ekleyen, kaldıran veya başka bir şekilde değiştiren işlevler için önemlidir. Üretim verilerinizin birim testleriniz tarafından beklenmeyen yollarla tehlikeye atılmasını korumak için üretim dışı verilere karşı birim testleri çalıştırmanız gerekir. Yaygın yaklaşımlardan biri, tablo yerine görünümlerde birim testleri çalıştırmaktır.

Görünüm oluşturmak için, create VIEW komutunu önceki not defterindeki yeni bir hücreden veya ayrı bir not defterinden çağırabilirsiniz. Aşağıdaki örnekte, adlı mainbir katalog içinde adlı diamonds bir şema (veritabanı) içinde adlı default mevcut bir tablonuz olduğu varsayılır. Bu adları gerektiği gibi kendi adlarınızla eşleşecek şekilde değiştirin ve ardından yalnızca bu hücreyi çalıştırın.

USE CATALOG main;
USE SCHEMA default;

CREATE VIEW view_diamonds AS
SELECT * FROM diamonds;

Görünümü oluşturduktan sonra, aşağıdaki SELECT deyimlerin her birini önceki not defterinde kendi yeni hücresine veya ayrı bir not defterindeki kendi yeni hücresine ekleyin. Adları gerektiği gibi kendi adlarınızla eşleşecek şekilde değiştirin.

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'."));

Birim testlerini çalıştırma

Bu bölümde, önceki bölümde kodladığınız birim testlerinin nasıl çalıştırıldığı açıklanmaktadır. Birim testlerini çalıştırdığınızda, hangi birim testlerinin geçirildiğini ve başarısız olduğunu gösteren sonuçlar alırsınız.

Önceki bölümden birim testlerini Azure Databricks çalışma alanınıza eklediyseniz, bu birim testlerini çalışma alanınızdan çalıştırabilirsiniz. Bu birim testlerini el ile veya bir zamanlamaya göre çalıştırabilirsiniz.

Python

Deponuzdaki önceki test_myfunctions.py dosyayla aynı klasörde bir Python not defteri oluşturun ve aşağıdaki içeriği ekleyin.

Yeni not defterinin ilk hücresine aşağıdaki kodu ekleyin ve ardından sihri çağıran hücreyi %pip çalıştırın. Bu sihirli yükler pytest.

%pip install pytest

İkinci hücreye aşağıdaki kodu ekleyin ve hücreyi çalıştırın. Sonuçlar hangi birim testlerinin geçtiğini ve başarısız olduğunu gösterir.

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

Deponuzdaki önceki test_myfunctions.r dosyayla aynı klasörde bir R not defteri oluşturun ve aşağıdaki içeriği ekleyin.

İlk hücreye aşağıdaki kodu ekleyin ve işlevi çağıran hücreyi install.packages çalıştırın. Bu işlev yükler testthat.

install.packages("testthat")

İkinci hücreye aşağıdaki kodu ekleyin ve ardından hücreyi çalıştırın. Sonuçlar hangi birim testlerinin geçtiğini ve başarısız olduğunu gösterir.

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

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

Scala

Önceki bölümden not defterinde birinci ve sonra ikinci hücreleri çalıştırın. Sonuçlar hangi birim testlerinin geçtiğini ve başarısız olduğunu gösterir.

SQL

Önceki bölümde yer alan not defterindeki üç hücrenin her birini çalıştırın. Sonuçlar, her birim testinin başarılı mı yoksa başarısız mı olduğunu gösterir.

Birim testlerinizi çalıştırdıktan sonra görünüme artık ihtiyacınız yoksa görünümü silebilirsiniz. Bu görünümü silmek için, aşağıdaki kodu önceki not defterlerinden birinin içindeki yeni bir hücreye ekleyebilir ve sonra yalnızca bu hücreyi çalıştırabilirsiniz.

DROP VIEW view_diamonds;

İpucu

Not defteri çalıştırmalarınızın sonuçlarını (birim testi sonuçları dahil) kümenizin sürücü günlüklerinde görüntüleyebilirsiniz. Kümenizin günlük teslimi için bir konum da belirtebilirsiniz.

Kodunuz her değiştiğinde birim testlerinizi otomatik olarak çalıştırmak için GitHub Actions gibi sürekli tümleştirme ve sürekli teslim veya dağıtım (CI/CD) sistemi ayarlayabilirsiniz. Bir örnek için, not defterleri için yazılım mühendisliği en iyi yöntemlerinde GitHub Actions'ın kapsamına bakın.

Ek kaynaklar

pytest

test edin

ScalaTest

SQL