astral-sh/uv
Testing
uv leans hard on integration tests with snapshot assertions. This page describes how the test infrastructure is laid out and how to add tests effectively.
Test layout
Tests are split between unit tests (in each crate's source as #[cfg(test)] mod tests) and
integration tests (in crates/uv/tests/it/, plus a few crate-local integration suites
under crates/<crate>/tests/).
Integration tests dominate. The crates/uv/tests/it/ directory contains:
| File | What it covers |
|---|---|
lock.rs (35,569 lines) |
uv lock behavior and lockfile output |
pip_compile.rs (18,574 lines) |
uv pip compile |
lock_conflict.rs (16,570 lines) |
Conflict marker handling in lockfiles |
sync.rs (16,268 lines) |
uv sync |
edit.rs (15,231 lines) |
uv add / uv remove / version edits |
pip_install.rs (15,136 lines) |
uv pip install |
show_settings.rs (11,702 lines) |
Settings resolution across config sources |
export.rs, tool_run.rs, version.rs, run.rs, … |
Other commands |
Each file is a single Rust source with hundreds of #[test] functions. Most use the
uv_snapshot! macro to assert on the full stdout/stderr/exit code of a uv invocation.
Running tests
The recommended runner is cargo nextest:
# Run a single test by name
$ cargo nextest run -E 'test(test_basic_add)'
# Run all tests
$ cargo nextest run --workspace
# Run tests in a single crate
$ cargo nextest run -p uv-resolverSnapshot management uses cargo-insta:
# Run and accept new snapshots
$ cargo insta test --accept --test-runner nextest
# Update snapshots for one test
$ cargo insta test --accept --test-runner nextest -- test_basic_add
# Review pending snapshot diffs
$ cargo insta reviewIf you've staged snapshot changes through CI rather than locally, use
./scripts/apply-ci-snapshots.sh to pull them into your working tree.
The uv_snapshot! macro
uv defines a custom uv_snapshot! macro for asserting CLI output. The pattern looks like:
#[test]
fn test_add() {
let context = TestContext::new("3.12");
uv_snapshot!(context.filters(), context.add().arg("requests"), @"");
}The macro:
- Runs the command using
assert_cmd::Command. - Captures stdout, stderr, and exit code.
- Applies the test context's filters (e.g., normalizing temp paths, durations, hashes).
- Compares against the inline snapshot string.
context.filters() is critical for stable output. Common filters strip:
- Temporary directory paths.
- Resolved durations (
Resolved 5 packages in 12ms). - Hash digests.
- OS-specific path separators.
Always copy the style of nearby tests in the same file when adding new cases.
Test contexts
Most tests construct a TestContext (defined in crates/uv/tests/it/common/). The context:
- Creates a fresh temp directory.
- Optionally provisions a Python interpreter at the requested version.
- Sets up a clean cache and config root.
- Provides builder methods (
context.add(),context.lock(),context.run(), …) that return pre-configuredassert_cmd::Commands with the right environment.
This keeps test functions short — most are 5 to 30 lines including the snapshot.
Python interpreters in tests
Tests that need Python use the test context's --python setup. To make sure the requested
versions are available locally:
$ cargo run python installuv's CI uses cargo run python install plus UV_PYTHON_INSTALL_DIR to provision a stable set
of interpreters before running the test suite.
The test fixtures cover CPython (multiple minor versions), PyPy, GraalPy, and the freethreaded CPython variant.
Git and Git LFS
Some tests exercise Git source distributions and need a working git binary. A smaller subset
also requires Git LFS. You can disable both via --no-default-features --features ... if your
environment can't provide them:
$ cargo nextest run --no-default-features --features other,relevant,featuresThe git and git-lfs features in crates/uv gate the relevant tests.
System Python tests
.github/workflows/test-system.yml runs uv against system Pythons on macOS, Linux, and
Windows. The fixtures are in scripts/check_system_python.py and scripts/check_registry.py.
These tests are not normally run locally — they verify behaviour against real installs you
might not have on hand.
Smoke and ecosystem tests
test-smoke.ymlruns a small, fast check on each platform that the binary works end to end.test-ecosystem.ymlruns uv against a curated list of real PyPI projects to catch regressions in resolution. The corpus lives inscripts/popular_packages/andtest/requirements/.
Trampoline tests
test-windows-trampolines.yml builds and runs the Windows trampoline binaries via QEMU/Wine
when full Windows runners aren't appropriate. The associated Rust integration tests live in
crates/uv-trampoline-builder/.
Updating dependencies
Tests can break when dependencies change. The AGENTS.md is explicit:
NEVER update all dependencies in the lockfile and ALWAYS use
cargo update --preciseto make lockfile changes.
Renovate handles routine bumps via PRs to main.
Profiling tests
For perf-sensitive changes, build with the profiling profile and use the
tracing-durations-export feature:
$ RUST_LOG=uv=info \
TRACING_DURATIONS_FILE=target/traces/jupyter.ndjson \
cargo run --features tracing-durations-export --profile profiling -- pip compile test/requirements/jupyter.inThe scripts/benchmark/ package provides higher-level harnesses. See
Tooling for details.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.