diff --git a/.gitignore b/.gitignore
index eeb8a6ec4087..a0f016bc5823 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 **/__pycache__
+.pytest_cache/
diff --git a/README.md b/README.md
index 75229421763c..c9c38eb5e98a 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,21 @@ environment file or folder (generated via `bitbake -c rootfs_wicenv
 - `src/bb/*`: various bitbake helpers that were brought along and used in other parts of wic.
 - `src/oe/*`: various oe-core helpers that were brought along and used in other parts of wic.
 
+## Testing
+
+wic's test suite lives under `tests/` and runs from a plain checkout
+with nothing but `pytest`; it needs no BitBake and no OpenEmbedded
+build. Install the test extras and run it:
+
+```
+pip install -e ".[tests]"
+just tests
+```
+
+The test extras include [just](https://just.systems/), the command
+runner used to drive the suite; run `just` on its own to list the
+available recipes.
+
 ## Contributing
 
 Please send all patches, comments, or questions to the yocto-patches
diff --git a/justfile b/justfile
new file mode 100644
index 000000000000..41a9d3d236f4
--- /dev/null
+++ b/justfile
@@ -0,0 +1,10 @@
+# wic test-suite command runner. Run `just` to list recipes.
+
+# List the available recipes.
+default:
+    @just --list
+
+# Run the test suite. Extra args pass through to pytest
+# (e.g. `just tests -k <expr>` or `just tests tests/unit`).
+tests *args:
+    pytest {{args}}
diff --git a/pyproject.toml b/pyproject.toml
index fdc1ce0f5ece..e60c4fcc1595 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -21,6 +21,12 @@ classifiers = [
 Homepage = "https://git.yoctoproject.org/wic"
 Repository = "https://git.yoctoproject.org/wic"
 
+[project.optional-dependencies]
+tests = [
+    "pytest >= 9.1.1",
+    "rust-just >= 1.55.1",
+]
+
 [project.scripts]
 wic = "wic.cli:main"
 
@@ -36,3 +42,11 @@ build-backend = "hatchling.build"
 
 [tool.hatch.version]
 path = "src/wic/cli.py"
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+# Keep a test's scratch directory only when that test fails, and keep
+# just the most recent failing session, so repeated runs do not pile up
+# leftover directories under the pytest base temp directory.
+tmp_path_retention_policy = "failed"
+tmp_path_retention_count  = 1
diff --git a/tests/unit/.gitkeep b/tests/unit/.gitkeep
new file mode 100644
index 000000000000..e69de29bb2d1
