diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9fc3e46b2d..bf53a3d2e9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -193,6 +193,7 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non
 - Use cargo 1.64 workspace inheritance feature. By @jinleili in [#3107](https://github.com/gfx-rs/wgpu/pull/3107)
 - Move `ResourceMetadata` into its own module. By @jimblandy in [#3213](https://github.com/gfx-rs/wgpu/pull/3213)
 - Add WebAssembly testing infrastructure. By @haraldreingruber in [#3238](https://github.com/gfx-rs/wgpu/pull/3238)
+- Error message when you forget to use cargo-nextest. By @cwfitzgerald in [#]()
 
 #### Vulkan
 
diff --git a/wgpu/tests/common/isolation.rs b/wgpu/tests/common/isolation.rs
new file mode 100644
index 0000000000..e4c7b6e560
--- /dev/null
+++ b/wgpu/tests/common/isolation.rs
@@ -0,0 +1,43 @@
+use std::sync::atomic::{AtomicBool, Ordering};
+
+/// True if a test is in progress somewhere in the process, false otherwise.
+static TEST_ACTIVE_IN_PROCESS: AtomicBool = AtomicBool::new(false);
+
+const OTHER_TEST_IN_PROGRESS_ERROR: &str = "TEST ISOLATION ERROR:
+
+wgpu's test harness requires that no more than one test is running per process.
+
+The best way to facilitate this is by using cargo-nextest which runs each test in its own process
+and has a very good testing UI:
+
+cargo install cargo-nextest
+cargo nextest run
+
+Alternatively, you can run tests in single threaded mode (much slower).
+
+cargo test -- --test-threads=1
+
+Calling std::process::abort()...
+";
+
+/// When this guard is active, enforces that there is only a single test running in the process
+/// at any one time. If there are multiple processes, creating the guard hard terminates the process.
+pub struct OneTestPerProcessGuard(());
+
+impl OneTestPerProcessGuard {
+    pub fn new() -> Self {
+        let other_tests_in_flight = TEST_ACTIVE_IN_PROCESS.swap(true, Ordering::SeqCst);
+        if other_tests_in_flight {
+            log::error!("{}", OTHER_TEST_IN_PROGRESS_ERROR);
+            // Hard exit to call attention to the error
+            std::process::abort();
+        }
+        OneTestPerProcessGuard(())
+    }
+}
+
+impl Drop for OneTestPerProcessGuard {
+    fn drop(&mut self) {
+        TEST_ACTIVE_IN_PROCESS.store(false, Ordering::SeqCst);
+    }
+}
diff --git a/wgpu/tests/common/mod.rs b/wgpu/tests/common/mod.rs
index e624fc7919..37fafed043 100644
--- a/wgpu/tests/common/mod.rs
+++ b/wgpu/tests/common/mod.rs
@@ -7,6 +7,7 @@ use wgpu::{Adapter, Device, DownlevelFlags, Instance, Queue, Surface};
 use wgt::{Backends, DeviceDescriptor, DownlevelCapabilities, Features, Limits};
 
 pub mod image;
+mod isolation;
 
 const CANVAS_ID: &str = "test-canvas";
 
@@ -167,6 +168,7 @@ impl TestParameters {
         self
     }
 }
+
 pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(TestingContext)) {
     // We don't actually care if it fails
     #[cfg(not(target_arch = "wasm32"))]
@@ -174,6 +176,8 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te
     #[cfg(target_arch = "wasm32")]
     let _ = console_log::init_with_level(log::Level::Info);
 
+    let _test_guard = isolation::OneTestPerProcessGuard::new();
+
     let (adapter, _) = initialize_adapter();
 
     let adapter_info = adapter.get_info();