How to Run Tests for Open-Notebook: Complete pytest & Async Guide
Open-Notebook uses pytest with async support; execute the full suite with uv run pytest from the repository root after installing dependencies via uv sync --dev.
The open-notebook project is an async-first Python application (Python 3.11+) that relies on pytest as its testing framework. Located in the lfnovo/open-notebook repository, the test suite validates domain logic, FastAPI endpoints, and SurrealDB integrations. This guide covers every command and architectural detail you need to execute tests locally and interpret results.
Prerequisites and Environment Setup
Before running tests, ensure your environment matches the project's requirements. The pyproject.toml specifies Python 3.11+ and uses uv as the package manager.
Install all development dependencies (including pytest, pytest-asyncio, and coverage tools):
uv sync --dev
This command reads the dependency declarations in pyproject.toml and installs the exact versions required for the test runner and async support.
Quick Commands to Run Tests for Open-Notebook
Execute these commands from the repository root to control test scope and output:
- Run the entire test suite:
uv run pytest - Run a single test file:
uv run pytest tests/test_models_api.py - Run a specific test function:
uv run pytest tests/test_models_api.py::test_create_notebook - Run only unit tests:
uv run pytest tests/unit/ - Generate coverage report:
uv run pytest --cov=open_notebook - Verbose output:
uv run pytest -v - Show printed output:
uv run pytest -s
The uv run prefix ensures the command executes within the project's virtual environment, isolating dependencies from your system Python.
Test Suite Architecture
The tests/ directory is organized into four distinct layers to separate concerns and execution speeds.
Unit Tests (tests/unit/)
These validate pure business-logic functions without external dependencies. They cover domain model validation in open_notebook/domain/, repository helpers, and utility functions. Unit tests execute quickly and require no database connection.
Integration Tests (tests/integration/)
Integration tests spin up the full FastAPI application (via httpx.AsyncClient) and a real SurrealDB instance to verify end-to-end flows. For example, a test might create a notebook, attach a source, and verify the persistence layer in a single async flow.
API Tests (tests/api/)
These directly target the FastAPI routers defined in api/routers/*.py. They assert HTTP status codes, validate JSON response payloads, and check error handling logic without mocking the underlying services.
Database Tests (tests/database/)
Focused on SurrealDB queries, migrations, and vector-search functionality, these tests ensure the persistence layer behaves correctly under concurrent async operations.
Working with Async Test Patterns
Since open-notebook is built on async/await patterns, every test file must use the @pytest.mark.asyncio decorator and await all async calls. As seen in tests/test_models_api.py (lines 18-20), this mirrors the production code's async design.
Example of a complete async API test:
# tests/api/test_notebooks_api.py
import pytest
from httpx import AsyncClient
from api.main import app # FastAPI instance
@pytest.mark.asyncio
async def test_create_notebook_via_api():
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.post(
"/api/notebooks",
json={"name": "My Notebook", "description": "Demo"},
)
assert response.status_code == 200
data = response.json()
assert data["name"] == "My Notebook"
Run this specific test in isolation:
uv run pytest tests/api/test_notebooks_api.py::test_create_notebook_via_api -v
Reusable Test Fixtures
The tests/conftest.py file defines shared fixtures that provide a clean async environment for every test. These fixtures handle temporary data population and automatic cleanup after each test run.
Example fixture for creating a temporary notebook:
# tests/conftest.py
import pytest
from open_notebook.domain.notebook import Notebook
@pytest.fixture
async def test_notebook():
nb = Notebook(name="Temp Notebook", description="Temp")
await nb.save()
yield nb
await nb.delete()
Any test function can now use await test_notebook to receive a fresh notebook instance without manual setup or teardown boilerplate.
Coverage Goals and Quality Gates
According to the official testing guide in docs/7-DEVELOPMENT/testing.md, the project maintains strict coverage thresholds:
- 70%+ overall code coverage
- 90%+ coverage for critical business logic in the domain layer
Generate a coverage report to verify these metrics locally:
uv run pytest --cov=open_notebook --cov-report=html
This creates an HTML report showing which lines in open_notebook/domain/ and other modules lack test coverage.
Summary
Running tests for open-notebook requires pytest with async support via pytest-asyncio.
- Execute
uv run pytestfrom the repository root to run the full suite against Python 3.11+. - Target specific layers using path arguments like
tests/unit/ortests/api/. - Leverage fixtures in
tests/conftest.pyfor clean async setup and teardown. - Use
@pytest.mark.asynciodecorators on all test functions that call async code. - Aim for 70%+ coverage, with 90%+ for domain logic, using the
--cov=open_notebookflag.
Frequently Asked Questions
What testing framework does open-notebook use?
Open-notebook uses pytest as the core runner with the pytest-asyncio plugin to handle async/await patterns. The configuration is defined in pyproject.toml, specifying asyncio_mode = auto to simplify test decoration.
How do I run tests without installing dependencies globally?
Use uv run pytest instead of pytest directly. The uv tool manages the virtual environment defined in pyproject.toml, ensuring all dev dependencies (including pytest-asyncio and httpx) are available without polluting your system Python.
Why do my tests fail with "async fixture" errors?
All async fixtures and test functions must use the @pytest.mark.asyncio decorator. Additionally, ensure you are using the async with pattern for clients (like httpx.AsyncClient) and awaiting all database calls, as demonstrated in tests/conftest.py and the API test examples.
Can I run tests against a real database?
Yes, the integration and database test layers in tests/integration/ and tests/database/ automatically spin up a SurrealDB instance. Ensure your environment has Docker available or a running SurrealDB server configured in your test environment variables before executing these specific test modules.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →