diff --git a/tests/test_versions/test_versions.py b/tests/test_versions/test_versions.py
new file mode 100644
index 0000000000000000000000000000000000000000..e7de0b1424ed6d84f9afb8da50b039cff1dea565
--- /dev/null
+++ b/tests/test_versions/test_versions.py
@@ -0,0 +1,120 @@
+import os
+import re
+import subprocess
+import time
+from pathlib import Path
+
+import pytest
+from run_iocsh import IOC
+
+TEST_DATA = Path(__file__).absolute().parent / "data"
+EPICS_BASE = os.environ.get("EPICS_BASE")
+E3_REQUIRE_VERSION = os.environ.get("E3_REQUIRE_VERSION")
+IOCSH_PATH = Path(EPICS_BASE) / "require" / E3_REQUIRE_VERSION / "bin" / "iocsh.bash"
+
+TEST_MODULE_NAME = "testversions"
+
+RE_MODULE_LOADED = f"Loaded {TEST_MODULE_NAME} version {{version}}"
+RE_MODULE_NOT_LOADED = f"Module {TEST_MODULE_NAME} (not available|version {{required}} not available|version {{required}} not available \\(but other versions are available\\))"
+
+CONFIG = f"""
+TOP:=$(CURDIR)
+
+# To configure require
+EPICS_BASE:=$(__EPICS_BASE_LOCATION)
+E3_REQUIRE_VERSION:=$(__REQUIRE_VERSION)
+
+E3_REQUIRE_LOCATION := $(EPICS_BASE)/require/$(E3_REQUIRE_VERSION)
+REQUIRE_CONFIG := $(E3_REQUIRE_LOCATION)/configure
+
+# To configure the modules
+EPICS_MODULE_NAME:={TEST_MODULE_NAME}
+E3_MODULE_VERSION:=$(__DEBUG_VERSION)
+E3_MODULE_NAME:=$(EPICS_MODULE_NAME)
+E3_MODULE_SRC_PATH:=$(EPICS_MODULE_NAME)
+E3_MODULE_MAKEFILE:=$(EPICS_MODULE_NAME).Makefile
+
+include $(REQUIRE_CONFIG)/CONFIG
+include $(REQUIRE_CONFIG)/RULES_SITEMODS
+
+.PHONY: db
+db:
+"""
+
+MODULE_MAKEFILE = """
+where_am_I := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
+include $(E3_REQUIRE_TOOLS)/driver.makefile
+
+DBDS      += ./test.dbd
+"""
+
+
+def run_make(path, *args, **kwargs):
+    test_env = os.environ.copy()
+    for kw in kwargs:
+        test_env[kw] = kwargs[kw]
+    make_cmd = ["make", "-C", path] + list(args)
+    p = subprocess.Popen(
+        make_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=test_env
+    )
+    outs, errs = p.communicate()
+    return p.returncode, outs, errs
+
+
+def run_ioc_get_output(version, cell_path):
+    with IOC("-r", f"{TEST_MODULE_NAME},{version}", "-l", cell_path) as ioc:
+        time.sleep(1)
+    return ioc.proc.returncode, ioc.outs, ioc.errs
+
+
+@pytest.fixture
+def wrapper(tmpdir):
+    wrapper_dir = Path(tmpdir / "wrapper")
+    test_dir = wrapper_dir / TEST_MODULE_NAME
+    test_dir.mkdir(parents=True)
+    with open(wrapper_dir / "Makefile", "w") as f:
+        f.write(CONFIG)
+    with open(wrapper_dir / f"{TEST_MODULE_NAME}.Makefile", "w") as f:
+        f.write(MODULE_MAKEFILE)
+    with open(wrapper_dir / TEST_MODULE_NAME / "test.dbd", "w") as f:
+        f.write("")
+    yield wrapper_dir
+
+
+@pytest.mark.parametrize(
+    "requested, expected, installed",
+    [
+        # Test versions can be loaded
+        ("test", "test", ["test", "0.0.1"]),
+        # Numeric versions should be prioritized over test versions
+        ("", "0.0.1+0", ["test", "0.0.1"]),
+        ("", "0.0.1+0", ["0.0.1", "test"]),
+        # Highest build number should be loaded if version is unspecified
+        ("", "0.0.1+7", ["0.0.1", "0.0.1+7", "0.0.1+3"]),
+        # Only load the given build number if it is specified
+        ("0.0.1+0", "", ["0.0.1+1"]),
+        # If no build number is specified, load the highest build number
+        ("0.0.1", "0.0.1+4", ["0.1.0", "0.0.1+4", "0.0.1"]),
+    ],
+)
+def test_version(wrapper, requested, expected, installed):
+    for version in installed:
+        returncode, _, _ = run_make(
+            wrapper,
+            "clean",
+            "cellinstall",
+            __EPICS_BASE_LOCATION=EPICS_BASE,
+            __REQUIRE_VERSION=E3_REQUIRE_VERSION,
+            __DEBUG_VERSION=version,
+        )
+        assert returncode == 0
+
+    rc, o, e = run_ioc_get_output(requested, wrapper / "cellMods")
+
+    if expected:
+        match = re.search(RE_MODULE_LOADED.format(version=re.escape(expected)), o)
+        assert match
+    else:
+        match = re.search(RE_MODULE_NOT_LOADED.format(required=re.escape(requested)), o)
+        assert match
+        assert rc != 0