Enhetstestning för notebook-filer

Du kan använda enhetstestning för att förbättra kvaliteten och konsekvensen i dina notebook-filers kod. Enhetstestning är en metod för att testa fristående kodenheter, till exempel funktioner, tidigt och ofta. Detta hjälper dig att hitta problem med koden snabbare, upptäcka felaktiga antaganden om din kod tidigare och effektivisera dina övergripande kodningsinsatser.

Den här artikeln är en introduktion till grundläggande enhetstestning med funktioner. Avancerade begrepp som enhetstestningsklasser och gränssnitt samt användning av stubs, mocks och testselar, som även stöds vid enhetstestning för notebook-filer, ligger utanför omfånget för den här artikeln. Den här artikeln omfattar inte heller andra typer av testmetoder, till exempel integreringstestning, systemtestning, acceptanstestning eller icke-funktionella testmetoder som prestandatestning eller användbarhetstestning.

Den här artikeln visar följande:

  • Så här organiserar du funktioner och deras enhetstester.
  • Så här skriver du funktioner i Python, R, Scala och användardefinierade funktioner i SQL, som är väl utformade för att vara enhetstestade.
  • Anropa dessa funktioner från Notebook-filer för Python, R, Scala och SQL.
  • Så här skriver du enhetstester i Python, R och Scala med hjälp av de populära testramverken pytest för Python, testthat för R och ScalaTest för Scala. Också hur man skriver SQL som enheten testar SQL användardefinierade funktioner (SQL UDF: er).
  • Så här kör du de här enhetstesterna från Python-, R-, Scala- och SQL-notebook-filer.

Organisera funktioner och enhetstester

Det finns några vanliga metoder för att organisera dina funktioner och deras enhetstester med notebook-filer. Varje metod har sina fördelar och utmaningar.

Vanliga metoder för Python-, R- och Scala-notebook-filer är följande:

  • Lagra funktioner och deras enhetstester utanför notebook-filer..
    • Fördelar: Du kan anropa dessa funktioner med och utanför notebook-filer. Testramverk är bättre utformade för att köra tester utanför notebook-filer.
    • Utmaningar: Den här metoden stöds inte för Scala-notebook-filer. Den här metoden ökar också antalet filer som ska spåras och underhållas.
  • Lagra funktioner i en notebook-fil och deras enhetstester i en separat notebook-fil..
    • Fördelar: Dessa funktioner är enklare att återanvända i notebook-filer.
    • Utmaningar: Antalet notebook-filer som ska spåras och underhållas ökar. Dessa funktioner kan inte användas utanför notebook-filer. Dessa funktioner kan också vara svårare att testa utanför notebook-filer.
  • Lagra funktioner och deras enhetstester i samma notebook-fil..
    • Fördelar: Funktioner och deras enhetstester lagras i en enda notebook-fil för enklare spårning och underhåll.
    • Utmaningar: Dessa funktioner kan vara svårare att återanvända i notebook-filer. Dessa funktioner kan inte användas utanför notebook-filer. Dessa funktioner kan också vara svårare att testa utanför notebook-filer.

För Python- och R-notebook-filer rekommenderar Databricks att du lagrar funktioner och deras enhetstester utanför notebook-filer. För Scala-notebook-filer rekommenderar Databricks att du inkluderar funktioner i en notebook-fil och deras enhetstester i en separat notebook-fil.

För SQL-notebook-filer rekommenderar Databricks att du lagrar funktioner som SQL-användardefinierade funktioner (SQL UDF:er) i dina scheman (även kallade databaser). Du kan sedan anropa dessa SQL UDF:er och deras enhetstester från SQL-notebook-filer.

Skrivfunktioner

I det här avsnittet beskrivs en enkel uppsättning exempelfunktioner som avgör följande:

  • Om en tabell finns i en databas.
  • Om en kolumn finns i en tabell.
  • Hur många rader som finns i en kolumn för ett värde i kolumnen.

Dessa funktioner är avsedda att vara enkla, så att du kan fokusera på enhetstestningsinformationen i den här artikeln i stället för att fokusera på själva funktionerna.

För att få de bästa enhetstestresultaten bör en funktion returnera ett enda förutsägbart resultat och vara av en enda datatyp. Om du till exempel vill kontrollera om något finns ska funktionen returnera ett booleskt värde av sant eller falskt. Om du vill returnera antalet rader som finns ska funktionen returnera ett icke-negativt heltal. Det bör inte i det första exemplet returnera antingen falskt om något inte finns eller själva saken om det finns. För det andra exemplet bör det inte heller returnera det antal rader som finns eller false om det inte finns några rader.

Du kan lägga till dessa funktioner i en befintlig Azure Databricks-arbetsyta enligt följande i Python, R, Scala eller SQL.

Python

Följande kod förutsätter att du har Konfigurera Databricks Git-mappar (Repos), lagt till en lagringsplats och har lagringsplatsen öppen på din Azure Databricks-arbetsyta.

Skapa en fil med namnet myfunctions.py på lagringsplatsen och lägg till följande innehåll i filen. Andra exempel i den här artikeln förväntar sig att den här filen får namnet myfunctions.py. Du kan använda olika namn för dina egna filer.

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

Följande kod förutsätter att du har Konfigurera Databricks Git-mappar (Repos), lagt till en lagringsplats och har lagringsplatsen öppen på din Azure Databricks-arbetsyta.

Skapa en fil med namnet myfunctions.r på lagringsplatsen och lägg till följande innehåll i filen. Andra exempel i den här artikeln förväntar sig att den här filen får namnet myfunctions.r. Du kan använda olika namn för dina egna filer.

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

Skapa en Scala-anteckningsbok med namnet myfunctions med följande innehåll. Andra exempel i den här artikeln förväntar sig att den här notebook-filen får namnet myfunctions. Du kan använda olika namn för dina egna notebook-filer.

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

Följande kod förutsätter att du har datamängdsdiamanter från tredje part i ett schema med namnet default i en katalog med namnet main som är tillgänglig från din Azure Databricks-arbetsyta. Om katalogen eller schemat som du vill använda har ett annat namn ändrar du en eller båda av följande USE instruktioner så att de matchar.

Skapa en SQL-notebook-fil och lägg till följande innehåll i den här nya notebook-filen. Koppla sedan anteckningsboken till ett kluster och kör anteckningsboken för att lägga till följande SQL UDF:er i den angivna katalogen och schemat.

Kommentar

SQL UDF:er table_exists och column_exists fungerar endast med Unity Catalog. SQL UDF-stöd för Unity Catalog finns i offentlig förhandsversion.

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

Anropsfunktioner

I det här avsnittet beskrivs kod som anropar föregående funktioner. Du kan till exempel använda dessa funktioner för att räkna antalet rader i tabellen där ett angivet värde finns i en specifikationskolumn. Du vill dock kontrollera om tabellen faktiskt finns och om kolumnen faktiskt finns i tabellen innan du fortsätter. Följande kod söker efter dessa villkor.

Om du har lagt till funktionerna från föregående avsnitt på din Azure Databricks-arbetsyta kan du anropa dessa funktioner från arbetsytan på följande sätt.

Python

Skapa en Python-anteckningsbok i samma mapp som föregående myfunctions.py fil på lagringsplatsen och lägg till följande innehåll i notebook-filen. Ändra variabelvärdena för tabellnamnet, schemats (databasens) namn, kolumnnamnet och kolumnvärdet efter behov. Koppla sedan anteckningsboken till ett kluster och kör notebook-filen för att se resultatet.

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

Skapa en R-anteckningsbok i samma mapp som föregående myfunctions.r fil på lagringsplatsen och lägg till följande innehåll i notebook-filen. Ändra variabelvärdena för tabellnamnet, schemats (databasens) namn, kolumnnamnet och kolumnvärdet efter behov. Koppla sedan anteckningsboken till ett kluster och kör notebook-filen för att se resultatet.

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

Skapa en annan Scala-anteckningsbok i samma mapp som föregående myfunctions Scala-anteckningsbok och lägg till följande innehåll i den nya notebook-filen.

I den här nya notebook-filens första cell lägger du till följande kod, som anropar %run magic. Den här magin gör innehållet i anteckningsboken myfunctions tillgängligt för din nya notebook-fil.

%run ./myfunctions

I den här nya notebook-filens andra cell lägger du till följande kod. Ändra variabelvärdena för tabellnamnet, schemats (databasens) namn, kolumnnamnet och kolumnvärdet efter behov. Koppla sedan anteckningsboken till ett kluster och kör notebook-filen för att se resultatet.

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

Lägg till följande kod i en ny cell i föregående notebook-fil eller till en cell i en separat notebook-fil. Ändra schema- eller katalognamnen om det behövs för att matcha ditt och kör sedan den här cellen för att se resultatet.

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

Skrivenhetstester

I det här avsnittet beskrivs kod som testar var och en av de funktioner som beskrivs i början av den här artikeln. Om du gör några ändringar i funktioner i framtiden kan du använda enhetstester för att avgöra om dessa funktioner fortfarande fungerar som du förväntar dig.

Om du har lagt till funktionerna i början av den här artikeln på din Azure Databricks-arbetsyta kan du lägga till enhetstester för dessa funktioner på din arbetsyta på följande sätt.

Python

Skapa en annan fil med namnet test_myfunctions.py i samma mapp som föregående myfunctions.py fil på lagringsplatsen och lägg till följande innehåll i filen. Som standard pytest letar .py efter filer vars namn börjar med test_ (eller slutar med _test) för att testa. På samma sätt letar som standard pytest i dessa filer efter funktioner vars namn börjar med test_ för att testa.

I allmänhet är det bästa praxis att inte köra enhetstester mot funktioner som fungerar med data i produktion. Detta är särskilt viktigt för funktioner som lägger till, tar bort eller på annat sätt ändrar data. För att skydda dina produktionsdata från att komprometteras av enhetstesterna på oväntade sätt bör du köra enhetstester mot icke-produktionsdata. En vanlig metod är att skapa falska data som ligger så nära produktionsdata som möjligt. I följande kodexempel skapas falska data som enhetstesterna ska köras mot.

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

Skapa en annan fil med namnet test_myfunctions.r i samma mapp som föregående myfunctions.r fil på lagringsplatsen och lägg till följande innehåll i filen. Som standard testthat letar .r efter filer vars namn börjar med test för att testa.

I allmänhet är det bästa praxis att inte köra enhetstester mot funktioner som fungerar med data i produktion. Detta är särskilt viktigt för funktioner som lägger till, tar bort eller på annat sätt ändrar data. För att skydda dina produktionsdata från att komprometteras av enhetstesterna på oväntade sätt bör du köra enhetstester mot icke-produktionsdata. En vanlig metod är att skapa falska data som ligger så nära produktionsdata som möjligt. I följande kodexempel skapas falska data som enhetstesterna ska köras mot.

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

Skapa en annan Scala-anteckningsbok i samma mapp som föregående myfunctions Scala-anteckningsbok och lägg till följande innehåll i den nya notebook-filen.

I den nya notebook-filens första cell lägger du till följande kod, som anropar magin %run . Den här magin gör innehållet i anteckningsboken myfunctions tillgängligt för din nya notebook-fil.

%run ./myfunctions

Lägg till följande kod i den andra cellen. Den här koden definierar enhetstesterna och anger hur de ska köras.

I allmänhet är det bästa praxis att inte köra enhetstester mot funktioner som fungerar med data i produktion. Detta är särskilt viktigt för funktioner som lägger till, tar bort eller på annat sätt ändrar data. För att skydda dina produktionsdata från att komprometteras av enhetstesterna på oväntade sätt bör du köra enhetstester mot icke-produktionsdata. En vanlig metod är att skapa falska data som ligger så nära produktionsdata som möjligt. I följande kodexempel skapas falska data som enhetstesterna ska köras mot.

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)

Kommentar

I det här kodexemplet används teststilen FunSuite i ScalaTest. Andra tillgängliga testformat finns i Välja testformat för projektet.

SQL

Innan du lägger till enhetstester bör du vara medveten om att det i allmänhet är en bra idé att inte köra enhetstester mot funktioner som fungerar med data i produktion. Detta är särskilt viktigt för funktioner som lägger till, tar bort eller på annat sätt ändrar data. För att skydda dina produktionsdata från att komprometteras av enhetstesterna på oväntade sätt bör du köra enhetstester mot icke-produktionsdata. En vanlig metod är att köra enhetstester mot vyer i stället för tabeller.

Om du vill skapa en vy kan du anropa kommandot CREATE VIEW från en ny cell i antingen den föregående notebook-filen eller en separat notebook-fil. I följande exempel förutsätts att du har en befintlig tabell med namnet diamonds i ett schema (databas) med namnet default i en katalog med namnet main. Ändra dessa namn så att de matchar dina egna efter behov och kör sedan bara cellen.

USE CATALOG main;
USE SCHEMA default;

CREATE VIEW view_diamonds AS
SELECT * FROM diamonds;

När du har skapat vyn lägger du till var och en av följande SELECT instruktioner i sin egen nya cell i föregående notebook-fil eller till en egen ny cell i en separat notebook-fil. Ändra namnen så att de matchar dina egna efter behov.

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

Köra enhetstester

I det här avsnittet beskrivs hur du kör de enhetstester som du kodade i föregående avsnitt. När du kör enhetstesterna får du resultat som visar vilka enhetstester som har godkänts och misslyckats.

Om du har lagt till enhetstesterna från föregående avsnitt på din Azure Databricks-arbetsyta kan du köra de här enhetstesterna från din arbetsyta. Du kan köra dessa enhetstester antingen manuellt eller enligt ett schema.

Python

Skapa en Python-anteckningsbok i samma mapp som föregående test_myfunctions.py fil på lagringsplatsen och lägg till följande innehåll.

I den nya notebook-filens första cell lägger du till följande kod och kör sedan cellen, som anropar magin %pip . Den här magin pytestinstallerar .

%pip install pytest

I den andra cellen lägger du till följande kod och kör sedan cellen. Resultaten visar vilka enhetstester som har godkänts och misslyckats.

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

Skapa en R-anteckningsbok i samma mapp som föregående test_myfunctions.r fil på lagringsplatsen och lägg till följande innehåll.

I den första cellen lägger du till följande kod och kör sedan cellen, som anropar install.packages funktionen. Den här funktionen installerar testthat.

install.packages("testthat")

I den andra cellen lägger du till följande kod och kör sedan cellen. Resultaten visar vilka enhetstester som har godkänts och misslyckats.

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

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

Scala

Kör den första och sedan andra cellerna i anteckningsboken från föregående avsnitt. Resultaten visar vilka enhetstester som har godkänts och misslyckats.

SQL

Kör var och en av de tre cellerna i anteckningsboken från föregående avsnitt. Resultaten visar om varje enhetstest har godkänts eller misslyckats.

Om du inte längre behöver vyn när du har kört enhetstesterna kan du ta bort vyn. Om du vill ta bort den här vyn kan du lägga till följande kod i en ny cell i någon av de föregående notebook-filerna och sedan bara köra cellen.

DROP VIEW view_diamonds;

Dricks

Du kan visa resultatet av dina notebook-körningar (inklusive enhetstestresultat) i klustrets drivrutinsloggar. Du kan också ange en plats för klustrets loggleverans.

Du kan konfigurera ett SYSTEM för kontinuerlig integrering och kontinuerlig leverans eller distribution (CI/CD), till exempel GitHub Actions, för att automatiskt köra enhetstesterna när koden ändras. Ett exempel finns i täckningen av GitHub Actions i Metodtips för programvaruutveckling för notebook-filer.

Ytterligare resurser

pytest

testthat

ScalaTest

SQL