diff --git a/.github/workflows/conda.yml b/.github/workflows/conda.yml
index 3b7f6a393c5c10d7b1d4aa531e5f4bd3987f7306..f32dba035776758704cdc082070ab594ef67eecf 100644
--- a/.github/workflows/conda.yml
+++ b/.github/workflows/conda.yml
@@ -32,7 +32,7 @@ jobs:
           channels: conda-forge
           
       - name: Prepare
-        run: conda install conda-build conda-verify
+        run: conda install conda-build conda-verify pytest
 
       - name: Build
         run: conda build conda.recipe
@@ -41,4 +41,4 @@ jobs:
         run: conda install -c ${CONDA_PREFIX}/conda-bld/ scikit_build_example
 
       - name: Test
-        run: python tests/test.py
+        run: pytest tests
diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml
index 23f391defbb522feddc3f9b4852d248a18911a09..9b0011d27377dd5bd81d4ad05e30036c3ce7d2e6 100644
--- a/.github/workflows/pip.yml
+++ b/.github/workflows/pip.yml
@@ -8,36 +8,13 @@ on:
       - master
 
 jobs:
-  make_dist:
-    name: Make distributions
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v2
-      with:
-        repository: pybind/pybind11
-
-    - uses: actions/setup-python@v2
-
-    - name: Download requirements
-      run: python -m pip install build
-
-    - name: Build files
-      run: |
-        python -m build
-        PYBIND11_GLOBAL_SDIST=ON python -m build
-
-    - uses: actions/upload-artifact@v2
-      with:
-        path: dist/*
-
   build:
     name: Build with Pip
-    needs: [make_dist]
     strategy:
       fail-fast: false
       matrix:
         platform: [windows-latest, macos-latest, ubuntu-latest]
-        python-version: ["2.7", "3.5", "3.8"]
+        python-version: ["2.7", "3.5", "3.9", "3.10-dev"]
 
         exclude:
           # Not supported by scikit-build
@@ -49,11 +26,6 @@ jobs:
     steps:
     - uses: actions/checkout@v2
 
-    - uses: actions/download-artifact@v2
-      with:
-        name: artifact
-        path: dist
-
     - uses: actions/setup-python@v2
       with:
         python-version: ${{ matrix.python-version }}
@@ -61,16 +33,8 @@ jobs:
     - name: Add requirements
       run: python -m pip install --upgrade wheel setuptools
 
-    # Eventually Microsoft might have an action for setting up
-    # MSVC, but for now, this action works:
-    - name: Prepare compiler environment for Windows 🐍 2.7
-      if: runner.os == 'Windows'
-      uses: ilammy/msvc-dev-cmd@v1.9.0
-      with:
-        arch: x64
-
     - name: Build and install
-      run: pip install --verbose . --find-links ${{ github.workspace }}/dist
+      run: pip install --verbose .[test]
 
     - name: Test
-      run: python tests/test.py
+      run: pytest
diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml
index 5533db202d862e5fd9f9928b9793770fc7327922..c97714217935edcc96ecc8571440b92680900960 100644
--- a/.github/workflows/wheels.yml
+++ b/.github/workflows/wheels.yml
@@ -11,7 +11,8 @@ on:
       - published
 
 env:
-  CIBW_TEST_COMMAND: python {project}/tests/test.py
+  CIBW_TEST_COMMAND: pytest {project}/tests
+  CIBW_TEST_EXTRAS: test
 
 
 jobs:
@@ -40,7 +41,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, macos-latest]  # Windows wheels currently not supported very well by scikit-build
+        os: [ubuntu-latest, macos-latest, windows-latest]
 
     steps:
     - uses: actions/checkout@v2
diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml
index f9acbbe4a3932fafdf3a86f715665df525091639..956c909e2b7b89c14de7dadd06e5fff6f5f1a82b 100644
--- a/conda.recipe/meta.yaml
+++ b/conda.recipe/meta.yaml
@@ -28,10 +28,12 @@ requirements:
 test:
   imports:
     - scikit_build_example
+  requires:
+    - pytest
   source_files:
     - tests
   commands:
-    - python tests/test.py
+    - pytest tests
 
 about:
   summary: An example project built with pybind11 and scikit-build.
diff --git a/noxfile.py b/noxfile.py
new file mode 100644
index 0000000000000000000000000000000000000000..31889fca940433dd4a0fb5ca0ba84f704c773f7c
--- /dev/null
+++ b/noxfile.py
@@ -0,0 +1,22 @@
+import nox
+
+
+nox.options.sessions = ["lint", "tests"]
+
+
+@nox.session
+def lint(session: nox.Session) -> None:
+    """
+    Run the linter.
+    """
+    session.install("pre-commit")
+    session.run("pre-commit", "run", "--all-files", *session.posargs)
+
+
+@nox.session
+def tests(session: nox.Session) -> None:
+    """
+    Run the unit and regular tests.
+    """
+    session.install(".[test]")
+    session.run("pytest", *session.posargs)
diff --git a/pyproject.toml b/pyproject.toml
index 88c314a554a2426fac8c3dcdf72532fcd9999e06..1d92f8bbaac1a90bb370950915d26a451e53a45b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -2,9 +2,9 @@
 requires = [
     "setuptools>=42",
     "wheel",
-    "pybind11~=2.6.0",
-    "cmake",
-    "scikit-build",
+    "pybind11>=2.7.1",
+    "cmake>=3.21",
+    "scikit-build>=0.12",
 ]
 
 build-backend = "setuptools.build_meta"
diff --git a/setup.py b/setup.py
index 8f4f111d36f12bb55f0323f6ce977a4c047f7744..b7cb87afbd21877492e377611cdc01de0483b941 100644
--- a/setup.py
+++ b/setup.py
@@ -27,4 +27,5 @@ setup(
     package_dir={"": "src"},
     cmake_install_dir="src/scikit_build_example",
     include_package_data = True,
+    extras_require={"test": ["pytest"]},
 )
diff --git a/tests/test.py b/tests/test.py
deleted file mode 100644
index 6e5a2ff92b735e3ac705161bafa7714eed2337f2..0000000000000000000000000000000000000000
--- a/tests/test.py
+++ /dev/null
@@ -1,6 +0,0 @@
-# -*- coding: utf-8 -*-
-import scikit_build_example as m
-
-assert m.__version__ == "0.0.1"
-assert m.add(1, 2) == 3
-assert m.subtract(1, 2) == -1
diff --git a/tests/test_basic.py b/tests/test_basic.py
new file mode 100644
index 0000000000000000000000000000000000000000..b79f885a12484046252dd033b2db7d152f6326aa
--- /dev/null
+++ b/tests/test_basic.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+import scikit_build_example as m
+
+
+def test_version():
+    assert m.__version__ == "0.0.1"
+
+
+def test_add():
+    assert m.add(1, 2) == 3
+
+
+def test_sub():
+    assert m.subtract(1, 2) == -1