Модульное тестирование ноутбуков
Вы можете использовать модульное тестирование, чтобы повысить качество и согласованность кода блокнотов. Модульное тестирование — это подход к тестированию самодостаточных единиц кода, таких как функции, на ранних этапах и часто. Это помогает быстрее найти проблемы с кодом, выявить ошибочные предположения о коде раньше и упростить общие усилия по программированию.
В этой статье приведены общие сведения о базовом модульном тестировании с функциями. Дополнительные понятия, такие как тестирование модулей классов и интерфейсов, а также использование заглушек , макетов и тестовых средств , хотя и поддерживаются при модульном тестировании записных книжек, выходят за рамки данной статьи. В этой статье также не рассматриваются другие методы тестирования, такие как тестирование интеграции, системное тестирование, приемочное тестированиеили нефункциональное тестирование, такие как тестирование производительности или тестирование удобства использования.
В этой статье показано следующее:
- Как упорядочивать функции и модульные тесты.
- Как создавать функции в Python, R, Scala, а также определяемые пользователем функции в SQL, которые хорошо предназначены для модульного тестирования.
- Как вызывать эти функции из записных книжек Python, R, Scala и SQL.
- Как создавать модульные тесты в Python, R и Scala с помощью популярных платформ тестирования pytest для Python, testthat для R и ScalaTest для ScalaTest. Как написать SQL, который выполняет модульное тестирование пользовательских функций SQL (UDF).
- Как выполнять эти модульные тесты из записных книжек Python, R, Scala и SQL.
Заметка
Azure Databricks рекомендует писать и запускать модульные тесты в записной книжке. Хотя вы можете выполнить некоторые команды в веб-терминале, веб-терминал имеет больше ограничений, таких как отсутствие поддержки Spark. См. выполнение команд оболочки в веб-терминале Azure Databricks.
Упорядочение функций и модульных тестов
Существует несколько распространенных подходов к организации функций и их модульных тестов с помощью записных книжек. Каждый подход имеет свои преимущества и проблемы.
Для записных книжек Python, R и Scala распространенные подходы включают следующие:
-
Храните функции и их модульные тесты вне блокнотов..
- Преимущества: Эти функции можно вызывать как внутри записных книжек, так и за их пределами. Платформы тестирования лучше предназначены для запуска тестов за пределами записных книжек.
- Проблемы. Этот подход не поддерживается для записных книжек Scala. Этот подход также увеличивает количество файлов для отслеживания и обслуживания.
-
Храните функции в одном блокноте и их модульные тесты в отдельном блокноте..
- Преимущества. Эти функции проще использовать в записных книжках.
- Проблемы: Количество записных книжек для отслеживания и обслуживания увеличивается. Эти функции нельзя использовать за пределами записных книжек. Эти функции также могут быть сложнее тестировать вне записных книжек.
-
Храните функции и их модульные тесты в одном ноутбуке..
- Преимущества. Функции и модульные тесты хранятся в одной записной книжке для упрощения отслеживания и обслуживания.
- Проблемы. Эти функции могут быть более сложными для повторного использования в записных книжках. Эти функции нельзя использовать за пределами записных книжек. Эти функции также могут быть сложнее тестировать вне записных книжек.
Для записных книжек Python и R Databricks рекомендует хранить функции и модульные тесты за пределами записных книжек. Для записных книжек Scala компания Databricks рекомендует размещать функции в одной записной книжке, а их модульные тесты — в отдельной.
Для тетрадей SQL Databricks рекомендует хранить функции как определяемые пользователем функции SQL (SQL UDF) в ваших схемах (также называемых базами данных). Затем вы можете вызвать эти определяемые пользователем функции SQL и их модульные тесты из блокнотов SQL.
Написать функции
В этом разделе описывается простой набор примеров функций, определяющих следующее:
- Существует ли таблица в базе данных.
- Существует ли столбец в таблице.
- Сколько строк существует в столбце для значения в этом столбце.
Эти функции предназначены для простоты, чтобы вы могли сосредоточиться на деталях модульного тестирования в этой статье, а не сосредоточиться на самих функциях.
Чтобы получить лучшие результаты модульного тестирования, функция должна возвращать один прогнозируемый результат и иметь один тип данных. Например, чтобы проверить, существует ли что-то, функция должна возвращать логическое значение true или false. Чтобы вернуть число существующих строк, функция должна возвращать не отрицательное целое число. В первом примере не должно возвращаться ни false, если что-то не существует, ни сам объект, если он существует. Аналогичным образом, во втором примере он не должен возвращать число строк, существующих или ложных, если строки отсутствуют.
Эти функции можно добавить в существующую рабочую область Azure Databricks, как показано ниже, в Python, R, Scala или SQL.
Питон
В следующем коде предполагается, что настроить папки Databricks Git (Repos), добавили репозиторийи откройте репозиторий в рабочей области Azure Databricks.
создайте файл с именем myfunctions.py
в репозитории и добавьте в файл следующее содержимое. Другие примеры в этой статье ожидают, что этот файл будет называться myfunctions.py
. Для собственных файлов можно использовать разные имена.
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
В следующем коде предполагается, что настроить папки Databricks Git (Repos), добавили репозиторийи откройте репозиторий в рабочей области Azure Databricks.
создайте файл с именем myfunctions.r
в репозитории и добавьте в файл следующее содержимое. Другие примеры в этой статье ожидают, что этот файл будет называться myfunctions.r
. Для собственных файлов можно использовать разные имена.
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
Создайте записную книжку на языке Scala с именем myfunctions
со следующим содержимым. Другие примеры в этой статье предполагают, что этот блокнот будет называться myfunctions
. Для собственных записных книжек можно использовать разные имена.
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
В следующем коде предполагается, что у вас есть сторонний образец данных diamonds в схеме с именем default
в каталоге с именем main
, который доступен из рабочей области Azure Databricks. Если каталог или схема, которую вы хотите использовать, имеет другое имя, измените одну или обе из следующих инструкций USE
, чтобы они соответствовали.
Создайте записную книжку SQL и добавьте в эту новую записную книжку следующее содержимое. Затем подключить записную книжку к кластеру и запустить записную книжку, чтобы добавить следующие пользовательские функции SQL в указанный каталог и схему.
Заметка
SQL UDF функции table_exists
и column_exists
работают только с Unity Catalog. Поддержка UDF SQL для каталога Unity находится в общедоступной предварительной версии.
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
Функции вызова
В этом разделе описывается код, вызывающий предыдущие функции. Эти функции можно использовать, например, для подсчета количества строк в таблице, в которой указанное значение существует в определенном столбце. Однако вы хотите проверить, существует ли таблица и существует ли столбец в этой таблице, прежде чем продолжить. Следующий код проверяет наличие этих условий.
Если вы добавили функции из предыдущего раздела в рабочую область Azure Databricks, эти функции можно вызвать из рабочей области следующим образом.
Питон
создать записную книжку Python в той же папке, что и предыдущий файл myfunctions.py
в репозитории, и добавить в записную книжку следующее содержимое. Измените значения переменных для имени таблицы, имени схемы (базы данных), имени столбца и значения столбца по мере необходимости. Затем подсоедините записную книжку к кластеру и запустите записную книжку, чтобы просмотреть результаты.
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
Создайте записную книжку R в той же папке, что и предыдущий файл myfunctions.r
в репозитории, и добавьте в записную книжку следующее содержимое. Измените значения переменных для имени таблицы, имени схемы (базы данных), имени столбца и значения столбца по мере необходимости. Затем подсоедините записную книжку к кластеру и запустите записную книжку, чтобы просмотреть результаты.
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
Создайте другую записную книжку Scala в той же папке, что и предыдущая myfunctions
записная книжка Scala, и добавьте в нее следующее содержимое.
В первой ячейке этой новой записной книжки добавьте следующий код, который вызывает %run магию. С помощью этого волшебства содержимое записной книжки myfunctions
становится доступным для вашей новой записной книжки.
%run ./myfunctions
В этой новой ячейке записной книжки добавьте следующий код. Измените значения переменных для имени таблицы, имени схемы (базы данных), имени столбца и значения столбца по мере необходимости. Затем подсоедините записную книжку к кластеру и запустите записную книжку, чтобы просмотреть результаты.
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
Добавьте следующий код в новую ячейку в предыдущей записной книжке или в ячейку в отдельной записной книжке. При необходимости измените имена схемы или каталога, чтобы они соответствовали вашим, а затем запустите эту ячейку, чтобы просмотреть результаты.
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
Написание модульных тестов
В этом разделе описывается код, который проверяет каждую из функций, описанных в начале этой статьи. При внесении изменений в функции в будущем можно использовать модульные тесты, чтобы определить, работают ли они по-прежнему.
Если вы добавили функции к началу этой статьи в рабочую область Azure Databricks, можно добавить модульные тесты для этих функций в рабочую область следующим образом.
Питон
Создайте другой файл с именем test_myfunctions.py
в той же папке, что и предыдущий файл myfunctions.py
в репозитории, и добавьте в него следующее содержимое. По умолчанию pytest
ищет файлы .py
, имена которых начинаются с test_
(или заканчиваются _test
) для тестирования. Аналогичным образом, по умолчанию pytest
выглядит внутри этих файлов для функций, имена которых начинаются с test_
для тестирования.
Как правило, рекомендуется не запускать юнит-тесты для функций, работающих с данными в продуктивной среде. Это особенно важно для функций, которые добавляют, удаляют или изменяют данные. Чтобы защитить рабочие данные от компрометации модульными тестами неожиданными способами, следует выполнить модульные тесты для непроизводственных данных. Одним из распространенных подходов является создание поддельных данных, которые максимально близки к рабочим данным. В следующем примере кода создаются поддельные данные для выполнения модульных тестов.
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
Создайте другой файл с именем test_myfunctions.r
в той же папке, что и предыдущий файл myfunctions.r
в репозитории, и добавьте в него следующее содержимое. По умолчанию testthat
ищет файлы .r
, имена которых начинаются с test
для тестирования.
Как правило, рекомендуется не запускать юнит-тесты для функций, работающих с данными в продуктивной среде. Это особенно важно для функций, которые добавляют, удаляют или изменяют данные. Чтобы защитить рабочие данные от компрометации модульными тестами неожиданными способами, следует выполнить модульные тесты для непроизводственных данных. Одним из распространенных подходов является создание поддельных данных, которые максимально близки к рабочим данным. В следующем примере кода создаются поддельные данные для выполнения модульных тестов.
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
Создайте другую записную книжку Scala в той же папке, что и предыдущая myfunctions
записная книжка Scala, и добавьте в нее следующее содержимое.
В первой ячейке нового блокнота добавьте следующий код, который вызывает магию %run
. С помощью этого волшебства содержимое записной книжки myfunctions
становится доступным для вашей новой записной книжки.
%run ./myfunctions
Во второй ячейке добавьте следующий код. Этот код определяет модульные тесты и указывает, как их запускать.
Как правило, рекомендуется не запускать юнит-тесты для функций, работающих с данными в продуктивной среде. Это особенно важно для функций, которые добавляют, удаляют или изменяют данные. Чтобы защитить рабочие данные от компрометации модульными тестами неожиданными способами, следует выполнить модульные тесты для непроизводственных данных. Одним из распространенных подходов является создание поддельных данных, которые максимально близки к рабочим данным. В следующем примере кода создаются поддельные данные для выполнения модульных тестов.
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)
Заметка
В этом примере кода используется стиль FunSuite
тестирования в ScalaTest. Другие доступные стили тестирования см. в разделе Выбор стилей тестирования для проекта.
SQL
Прежде чем добавлять модульные тесты, следует помнить, что в целом рекомендуется не запускать модульные тесты для функций, работающих с данными в рабочей среде. Это особенно важно для функций, которые добавляют, удаляют или изменяют данные. Чтобы защитить рабочие данные от компрометации модульными тестами неожиданными способами, следует выполнить модульные тесты для непроизводственных данных. Одним из распространенных подходов является запуск модульных тестов для представлений вместо таблиц.
Чтобы создать представление, можно вызвать команду CREATE VIEW из новой ячейки в предыдущей записной книжке или отдельной записной книжке. В следующем примере предполагается, что у вас есть существующая таблица с именем diamonds
в схеме (базе данных) с именем default
в каталоге с именем main
. Измените эти имена, чтобы они соответствовали вашим собственным, а затем выполните только эту ячейку.
USE CATALOG main;
USE SCHEMA default;
CREATE VIEW view_diamonds AS
SELECT * FROM diamonds;
После создания представления добавьте каждую из следующих инструкций SELECT
в свою новую ячейку в предыдущей записной книжке или в собственную ячейку в отдельной записной книжке. Измените имена, чтобы они соответствовали вашим собственным по мере необходимости.
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'."));
Выполнение модульных тестов
В этом разделе описывается, как выполнять модульные тесты, закодированные в предыдущем разделе. При выполнении модульных тестов вы получите результаты, показывающие, какие модульные тесты прошли и какие не прошли.
Если вы добавили модульные тесты из предыдущего раздела в рабочую область Azure Databricks, вы можете выполнить эти модульные тесты из рабочей области. Эти модульные тесты можно запускать вручную или по расписанию.
Питон
Создайте записную книжку Python в той же папке, что и предыдущий файл test_myfunctions.py
в репозитории, и добавьте следующее содержимое.
В первой ячейке новой записной книжки добавьте следующий код, а затем запустите ячейку, которая вызывает магию %pip
. Эта магия устанавливает pytest
.
%pip install pytest
Во второй ячейке добавьте следующий код и запустите ячейку. Результаты показывают, какие модульные тесты прошли, а какие не прошли.
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
Создайте записную книжку R в той же папке, что и предыдущий файл test_myfunctions.r
в репозитории, и добавьте следующее содержимое.
В первой ячейке добавьте следующий код, а затем запустите ячейку, которая вызывает функцию install.packages
. Эта функция устанавливает testthat
.
install.packages("testthat")
Во второй ячейке добавьте следующий код и запустите ячейку. Результаты показывают, какие модульные тесты прошли, а какие не прошли.
library(testthat)
source("myfunctions.r")
test_dir(".", reporter = "tap")
Scala
Запустите сначала первую, а затем вторую ячейки в записной книжке из предыдущего раздела. Результаты показывают, какие модульные тесты прошли, а какие не прошли.
SQL
Запустите каждую из трех ячеек записной книжки из предыдущего раздела. Результаты показывают, прошел ли каждый модульный тест или завершился сбоем.
Если после выполнения модульных тестов представление больше не требуется, его можно удалить. Чтобы удалить это представление, можно добавить следующий код в новую ячейку в одной из предыдущих записных книжек, а затем запустить только эту ячейку.
DROP VIEW view_diamonds;
Совет
Результаты выполнения записной книжки (включая результаты модульных тестов) можно просмотреть в логах драйвера вашего кластера. Вы также можете указать местоположение для доставки журналов кластера.
Вы можете настроить систему непрерывной интеграции и непрерывной доставки или развертывания (CI/CD), например GitHub Actions, для автоматического запуска модульных тестов при изменении кода. Пример см. в статье о охвате GitHub Actions в рекомендации по проектированию программного обеспечения для записных книжек.
Дополнительные ресурсы
pytest
- домашняя страница pytest
- руководства по pytest
- справочные руководства pytest
- рекомендации по проектированию программного обеспечения для записных книжек
testthat
- домашняя страница testthat
- Справочник по функции testthat
ScalaTest
- домашняя страница ScalaTest
- Руководство пользователя ScalaTest
- документация по Scaladoc в Scaladoc
ScalaTest