From qa-data-notebooks
Use the `@testbook` decorator to write conventional pytest unit tests against functions defined in Jupyter notebooks, without copy-pasting the function into a `.py` file. Pairs with `tb.ref()` (notebook object access) and `tb.inject()` (insert code into kernel) for hermetic per-test setup.
How this skill is triggered — by the user, by Claude, or both
Slash command
/qa-data-notebooks:testbook-testsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
testbook is a *"unit testing framework for testing code in Jupyter
testbook is a "unit testing framework for testing code in Jupyter
Notebooks" per the testbook docs. It treats .ipynb files like
.py files for testing - the test runs separately from the notebook
itself.
.py module.nbval (full-notebook regression) and others use testbook
(function-level).pip install testbook pytest
Per the testbook docs.
Given a notebook cell:
def func(a, b):
return a + b
Test:
from testbook import testbook
@testbook('/path/to/notebook.ipynb', execute=True)
def test_func(tb):
func = tb.ref("func")
assert func(1, 2) == 3
Per the testbook docs: the decorator accepts the notebook path +
execute parameter controlling cell execution.
tb.ref() for notebook objects@testbook('analysis.ipynb', execute=True)
def test_clean_data_drops_nulls(tb):
clean_data = tb.ref("clean_data")
df = tb.ref("pd").DataFrame({"a": [1, None, 3]})
result = clean_data(df)
assert len(result) == 2
tb.ref() returns a proxy to the notebook-side object - calls run
in the kernel.
tb.inject() for setup code@testbook('model.ipynb', execute=True)
def test_predict_with_specific_input(tb):
tb.inject(
"""
import numpy as np
np.random.seed(42)
test_input = np.array([[1.0, 2.0, 3.0]])
"""
)
predict = tb.ref("predict")
result = predict(tb.ref("test_input"))
assert result.shape == (1,)
tb.inject() runs arbitrary code in the kernel - useful for
deterministic seeding, mocking globals, fixture setup.
# Execute only specific cells (by tag)
@testbook('notebook.ipynb', execute=['imports', 'data-load'])
def test_with_partial_execution(tb):
df = tb.ref("df")
assert len(df) > 0
Avoids slow training cells when testing pure-function helpers.
import pytest
from testbook import testbook
@pytest.fixture(scope="module")
def tb():
with testbook('/path/to/notebook.ipynb', execute=True) as tb:
yield tb
def test_func_a(tb):
assert tb.ref("func_a")(1) == 2
def test_func_b(tb):
assert tb.ref("func_b")("x") == "X"
Per the testbook docs: shared kernel context across tests via pytest fixtures - much faster than re-executing the notebook per test.
@testbook('api_client.ipynb', execute=True)
def test_handle_api_failure(tb):
with tb.patch('requests.get') as mock_get:
mock_get.return_value.status_code = 500
result = tb.ref("fetch_data")()
assert result == {"error": "API failed"}
tb.patch() mirrors unittest.mock.patch but operates inside the
notebook's kernel.
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Re-execute notebook per test | Slow; minutes per CI run | Use module-scoped fixture (Step 6) |
Use tb.ref() to fetch large DataFrames into test process | Serialization overhead; "non-serializable value" errors | Operate on the proxy via tb.ref() calls; only fetch primitives |
Skip execute=True and assume cells already ran | Notebook variables may not exist | Always execute=True (or scoped fixture) |
| Mix testbook + nbval on same notebook | Conflicting kernel sessions | Use one tool per notebook (or separate workflow runs) |
Inject arbitrary state via tb.inject for prod code | Tests pass but real notebook fails | Inject only test-specific setup (seeds, fixtures) |
execute=True slow tests; use selective
execution (Step 5) when possible.tb.ref() cleanly; refactor to return
primitives.@testbook decorator, tb.ref(), tb.inject(),
tb.patch(), pytest fixture patternProvides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Searches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
npx claudepluginhub testland/qa --plugin qa-data-notebooks