我正在尝试想出一种方法来测试多个 Jupyter 笔记本。当在 Github 分支中实现新笔记本并提交拉取请求时,应该运行测试。测试并不那么复杂,它们主要只是测试笔记本是否端到端运行并且没有任何错误,也许还有一些断言。然而:
我愿意使用任何测试库,例如“pytest”或
unittest
,尽管pytest
是首选。
我查看了一些用于测试笔记本的库,例如 nbmake、treon 和 testbook,但我无法使它们工作。我还尝试将笔记本转换为 python 文件,但神奇单元被转换为
get_ipython().run_cell_magic(...)
调用,这成为了一个问题,因为 pytest
使用 python 而不是 ipython,并且 get_ipython()
仅在 ipython 中可用。
所以,我想知道考虑到所有这些,测试 Jupyter Notebook 的好方法是什么。如有任何帮助,我们将不胜感激。
nbconvert
执行整个笔记本。
笔记本
failed.ipynb
引发异常将导致运行失败,这要归功于--execute
选项告诉nbconvert
在转换之前执行笔记本。
jupyter nbconvert --to notebook --execute failed.ipynb
# ...
# Exception: FAILED
echo $?
# 1
另一个正确的笔记本
passed.ipynb
将导致导出成功。
jupyter nbconvert --to notebook --execute passed.ipynb
# [NbConvertApp] Converting notebook passed.ipynb to notebook
# [NbConvertApp] Writing 1172 bytes to passed.nbconvert.ipynb
echo $?
# 0
锦上添花,您可以通过 API 做同样的事情,然后将其包装在 Pytest 中!
import nbformat
import pytest
from nbconvert.preprocessors import ExecutePreprocessor
@pytest.mark.parametrize("notebook", ["passed.ipynb", "failed.ipynb"])
def test_notebook_exec(notebook):
with open(notebook) as f:
nb = nbformat.read(f, as_version=4)
ep = ExecutePreprocessor(timeout=600, kernel_name='python3')
try:
assert ep.preprocess(nb) is not None, f"Got empty notebook for {notebook}"
except Exception:
assert False, f"Failed executing {notebook}"
运行测试给出。
pytest test_nbconv.py
# FAILED test_nbconv.py::test_notebook_exec[failed.ipynb] - AssertionError: Failed executing failed.ipynb
# PASSED test_nbconv.py::test_notebook_exec[passed.ipynb]
注释notebook
。
这本身不会将笔记本转换为不同的格式,而是允许在笔记本上运行 nbconvert 预处理器,和/或转换为其他笔记本格式。
testbook 的解决方案。假设我有一个名为 my_notebook.ipynb
的笔记本,其中包含以下内容:诀窍是在我调用
bigquery.Client
之前注入一个单元格并模拟它:
from testbook import testbook
@testbook('./my_notebook.ipynb')
def test_get_details(tb):
tb.inject(
"""
import mock
mock_client = mock.MagicMock()
mock_df = pd.DataFrame()
mock_df['week'] = range(10)
mock_df['count'] = 5
p1 = mock.patch.object(bigquery, 'Client', return_value=mock_client)
mock_client.query().result().to_dataframe.return_value = mock_df
p1.start()
""",
before=2,
run=False
)
tb.execute()
dataframe = tb.get('dataframe')
assert dataframe.shape == (10, 2)
x = tb.get('x')
assert x == 7
nbval
(位于https://nbval.readthedocs.io/en/latest/)或https://github.com/computationalmodelling/nbval作为源代码。它是
pytest
的扩展。基本思想是执行笔记本并将计算的输出与笔记本文件中保存的输出进行比较。每个单元都被视为一个测试:如果单元重新计算的输出与磁盘上的输出匹配,则认为测试通过。 (否则失败。)
可以使用以下命令运行测试:
$ py.test --nbval my_notebook.ipynb
在 lax
模式下,单元格的输出将被忽略,只要没有引发异常,笔记本就会通过。这是检查任何界面更改是否损坏笔记本的便捷方法(适合测试基于笔记本的文档):
$ py.test --nbval-lax my_notebook.ipynb
可以跳过特定的单元格,或者忽略它们产生的输出,或者期望引发异常。还可以定义(正则表达式)异常,以实际上忽略重复执行中预期不同的输出更改(例如内存地址、执行日期和时间或运行时间)。我认为嘲笑是不可能的。
该软件包可在 PyPI 上使用 (
https://pypi.org/project/nbval/)。