diff --git a/package_manager/tests/src/Build/TemplateProjectTestBase.php b/package_manager/tests/src/Build/TemplateProjectTestBase.php
index 188692753dc216a64a330b1b7019535c337e3c46..47ecdac12c02bd9ba9be3faba583571de97f1c60 100644
--- a/package_manager/tests/src/Build/TemplateProjectTestBase.php
+++ b/package_manager/tests/src/Build/TemplateProjectTestBase.php
@@ -14,6 +14,7 @@ use Drupal\package_manager\Event\PreApplyEvent;
 use Drupal\package_manager\Event\PreCreateEvent;
 use Drupal\package_manager\Event\PreDestroyEvent;
 use Drupal\package_manager\Event\PreRequireEvent;
+use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait;
 use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait;
 use Drupal\Tests\RandomGeneratorTrait;
 
@@ -29,6 +30,7 @@ use Drupal\Tests\RandomGeneratorTrait;
  */
 abstract class TemplateProjectTestBase extends QuickStartTestBase {
 
+  use AssertPreconditionsTrait;
   use FixtureUtilityTrait;
   use RandomGeneratorTrait;
 
diff --git a/package_manager/tests/src/Functional/FailureMarkerRequirementTest.php b/package_manager/tests/src/Functional/FailureMarkerRequirementTest.php
index 1f5f59c3a2e10c244610f7f7f8181e5cd8f71363..16b1ad29a7a0657cce26669f77245f93adf9c7b8 100644
--- a/package_manager/tests/src/Functional/FailureMarkerRequirementTest.php
+++ b/package_manager/tests/src/Functional/FailureMarkerRequirementTest.php
@@ -7,6 +7,7 @@ namespace Drupal\Tests\package_manager\Functional;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\package_manager\Stage;
 use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait;
 
 /**
  * Tests that Package Manager's requirements check for the failure marker.
@@ -17,6 +18,8 @@ use Drupal\Tests\BrowserTestBase;
 class FailureMarkerRequirementTest extends BrowserTestBase {
   use StringTranslationTrait;
 
+  use AssertPreconditionsTrait;
+
   /**
    * {@inheritdoc}
    */
diff --git a/package_manager/tests/src/Kernel/ComposerUtilityTest.php b/package_manager/tests/src/Kernel/ComposerUtilityTest.php
index 98906327540e25fa558c89c411b3081d3145325b..2a9187f73e249b96e11b40eb918f6f5490add76f 100644
--- a/package_manager/tests/src/Kernel/ComposerUtilityTest.php
+++ b/package_manager/tests/src/Kernel/ComposerUtilityTest.php
@@ -7,6 +7,7 @@ namespace Drupal\Tests\package_manager\Kernel;
 use Drupal\fixture_manipulator\FixtureManipulator;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\package_manager\ComposerUtility;
+use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait;
 use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait;
 use org\bovigo\vfs\vfsStream;
 
@@ -17,6 +18,7 @@ use org\bovigo\vfs\vfsStream;
  */
 class ComposerUtilityTest extends KernelTestBase {
 
+  use AssertPreconditionsTrait;
   use FixtureUtilityTrait;
 
   /**
diff --git a/package_manager/tests/src/Kernel/CorePackageManifestTest.php b/package_manager/tests/src/Kernel/CorePackageManifestTest.php
index e5ededf42a3a90c5873ee4e6a00949a85f30fb6d..12596e0d262a43498e5769b77c0f55f0f116fea5 100644
--- a/package_manager/tests/src/Kernel/CorePackageManifestTest.php
+++ b/package_manager/tests/src/Kernel/CorePackageManifestTest.php
@@ -7,6 +7,7 @@ namespace Drupal\Tests\package_manager\Kernel;
 use Drupal\Component\Serialization\Json;
 use Drupal\Component\Serialization\Yaml;
 use Drupal\KernelTests\KernelTestBase;
+use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait;
 use Symfony\Component\Finder\Finder;
 
 /**
@@ -26,6 +27,8 @@ use Symfony\Component\Finder\Finder;
  */
 class CorePackageManifestTest extends KernelTestBase {
 
+  use AssertPreconditionsTrait;
+
   /**
    * Tests that detected core packages match our hard-coded manifest file.
    */
diff --git a/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php b/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php
index 14e9e7da9eab102f6038bf2f07efb8ac453b15cb..c0b2f335d62c2b81dec98ad36c5b4adfca7d98aa 100644
--- a/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php
+++ b/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php
@@ -5,6 +5,7 @@ declare(strict_types = 1);
 namespace Drupal\Tests\package_manager\Kernel;
 
 use Drupal\KernelTests\KernelTestBase;
+use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait;
 use PhpTuf\ComposerStager\Domain\Service\FileSyncer\FileSyncerInterface;
 use PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\PhpFileSyncer;
 use PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\RsyncFileSyncer;
@@ -16,6 +17,8 @@ use PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\RsyncFileSyncer;
  */
 class FileSyncerFactoryTest extends KernelTestBase {
 
+  use AssertPreconditionsTrait;
+
   /**
    * {@inheritdoc}
    */
diff --git a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
index 266015ec8f6ee39e35319e965abfc3acf7e68b14..a54d40d60da29640a95d32987b9daf0c4e82b466 100644
--- a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
+++ b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
@@ -14,6 +14,7 @@ use Drupal\package_manager\Exception\StageException;
 use Drupal\package_manager\Exception\StageValidationException;
 use Drupal\package_manager\Stage;
 use Drupal\package_manager_bypass\Beginner;
+use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait;
 use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait;
 use Drupal\Tests\package_manager\Traits\ValidationTestTrait;
 use GuzzleHttp\Client;
@@ -35,6 +36,7 @@ use Symfony\Component\DependencyInjection\Definition;
  */
 abstract class PackageManagerKernelTestBase extends KernelTestBase {
 
+  use AssertPreconditionsTrait;
   use FixtureUtilityTrait;
   use StatusCheckTrait;
   use ValidationTestTrait;
diff --git a/package_manager/tests/src/Kernel/ServicesTest.php b/package_manager/tests/src/Kernel/ServicesTest.php
index 33e3cc72874b3fe9ee2d38fdd46b3194b5929eef..e9d81754f08729faaebabd1b2b5088050e42cf46 100644
--- a/package_manager/tests/src/Kernel/ServicesTest.php
+++ b/package_manager/tests/src/Kernel/ServicesTest.php
@@ -7,6 +7,7 @@ namespace Drupal\Tests\package_manager\Kernel;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\package_manager\ExecutableFinder;
 use Drupal\package_manager\ProcessFactory;
+use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait;
 use PhpTuf\ComposerStager\Infrastructure\Factory\Process\ProcessFactoryInterface;
 use PhpTuf\ComposerStager\Infrastructure\Service\Finder\ExecutableFinderInterface;
 
@@ -18,6 +19,8 @@ use PhpTuf\ComposerStager\Infrastructure\Service\Finder\ExecutableFinderInterfac
  */
 class ServicesTest extends KernelTestBase {
 
+  use AssertPreconditionsTrait;
+
   /**
    * {@inheritdoc}
    */
diff --git a/package_manager/tests/src/Traits/AssertPreconditionsTrait.php b/package_manager/tests/src/Traits/AssertPreconditionsTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..1f6bfb3d4befa4a04409d2481cf420133c397db1
--- /dev/null
+++ b/package_manager/tests/src/Traits/AssertPreconditionsTrait.php
@@ -0,0 +1,109 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\Tests\package_manager\Traits;
+
+use Composer\Autoload\ClassLoader;
+
+/**
+ * Asserts preconditions for tests to function properly.
+ */
+trait AssertPreconditionsTrait {
+
+  /**
+   * Invokes the test preconditions assertion before the first test is run.
+   *
+   * "Use" this trait on any Automatic Updates class that directly extends a
+   * Core test class, i.e., any class that does NOT extend a test class in a
+   * Automatic Updates test namespace. If that class implements this hook, too,
+   * be sure to call this first thing in it.
+   */
+  public static function setUpBeforeClass(): void {
+    parent::setUpBeforeClass();
+    static::failIfUnmetPreConditions('before');
+  }
+
+  /**
+   * Invokes the test preconditions assertion after each test run.
+   *
+   * This ensures that no test method leaves behind violations of test
+   * preconditions. This makes it trivial to discover broken tests.
+   */
+  protected function tearDown(): void {
+    parent::tearDown();
+    static::failIfUnmetPreConditions('after');
+  }
+
+  /**
+   * Asserts universal test preconditions before any setup is done.
+   *
+   * If these preconditions aren't met, automated tests will absolutely fail
+   * needlessly with misleading errors. In that case, there's no reason to even
+   * begin.
+   *
+   * Ordinarily, these preconditions would be asserted in
+   * ::assertPreConditions(), which PHPUnit provides for exactly this use case.
+   * Unfortunately, that method doesn't run until after ::setUp(), so our (many)
+   * tests with expensive, time-consuming setup routines wouldn't actually fail
+   * very early.
+   *
+   * @param string $when
+   *   Either 'before' (before any test methods run) or 'after' (after any test
+   *   method finishes).
+   *
+   * @see \PHPUnit\Framework\TestCase::assertPreConditions()
+   * @see \PHPUnit\Framework\TestCase::setUpBeforeClass()
+   * @see self::setupBeforeClass()
+   * @see self::tearDown()
+   */
+  protected static function failIfUnmetPreConditions(string $when): void {
+    assert(in_array($when, ['before', 'after'], TRUE));
+    static::assertNoFailureMarker($when);
+  }
+
+  /**
+   * Asserts that there is no failure marker present.
+   *
+   * @param string $when
+   *   Either 'before' (before any test methods run) or 'after' (after any test
+   *   method finishes).
+   *
+   * @see \Drupal\package_manager\FailureMarker
+   */
+  private static function assertNoFailureMarker(string $when): void {
+    // If the failure marker exists, it will be in the project root. The project
+    // root is defined as the directory containing the `vendor` directory.
+    // @see \Drupal\package_manager\FailureMarker::getPath()
+    $failure_marker = static::getProjectRoot() . '/PACKAGE_MANAGER_FAILURE.json';
+    if (file_exists($failure_marker)) {
+      $suffix = $when === 'before'
+        ? 'Remove it to continue.'
+        : 'This test method created this marker but failed to clean up after itself.';
+      static::fail("The failure marker '$failure_marker' is present in the project. $suffix");
+    }
+  }
+
+  /**
+   * Returns the absolute path of the project root.
+   *
+   * @return string
+   *   The absolute path of the project root.
+   *
+   * @see \Drupal\package_manager\PathLocator::getProjectRoot()
+   */
+  private static function getProjectRoot(): string {
+    // This is tricky, because this method has to be static (since
+    // ::setUpBeforeClass is), so it can't just get the container from an
+    // instance member.
+    // Use reflection to extract the vendor directory from the class loader.
+    $class_loader = $GLOBALS['loader'];
+    assert($class_loader instanceof ClassLoader);
+    $object = new \ReflectionObject($class_loader);
+    $property = $object->getProperty('vendorDir');
+    $property->setAccessible(TRUE);
+    $vendor_directory = $property->getValue($class_loader);
+    return dirname($vendor_directory);
+  }
+
+}
diff --git a/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php b/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php
index 608b5311c4a491c506826222bce897e95d57cfdd..14761deb05ecb287cb93b60857f6a2e24f3a5608 100644
--- a/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php
+++ b/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php
@@ -10,6 +10,7 @@ use Drupal\fixture_manipulator\StageFixtureManipulator;
 use Drupal\package_manager_bypass\Beginner;
 use Drupal\package_manager_bypass\Stager;
 use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait;
 use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -20,6 +21,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  */
 abstract class AutomaticUpdatesFunctionalTestBase extends BrowserTestBase {
 
+  use AssertPreconditionsTrait;
   use FixtureUtilityTrait;
 
   /**
diff --git a/tests/src/Functional/HelpPageTest.php b/tests/src/Functional/HelpPageTest.php
index bfe81a41b1fd1d43278992e3e9a791ab02e36db9..9e32ed8f8109751e0184b2a058a3613ac89ad7de 100644
--- a/tests/src/Functional/HelpPageTest.php
+++ b/tests/src/Functional/HelpPageTest.php
@@ -5,6 +5,7 @@ declare(strict_types = 1);
 namespace Drupal\Tests\automatic_updates\Functional;
 
 use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait;
 
 /**
  * @group automatic_updates
@@ -12,6 +13,8 @@ use Drupal\Tests\BrowserTestBase;
  */
 class HelpPageTest extends BrowserTestBase {
 
+  use AssertPreconditionsTrait;
+
   /**
    * {@inheritdoc}
    */
diff --git a/tests/src/Functional/UpdatePathTest.php b/tests/src/Functional/UpdatePathTest.php
index b697f42c18305137e9f370592b6bd0eb38d0747b..329e34fb1eae4b80bf169f9f5852470493eea0cd 100644
--- a/tests/src/Functional/UpdatePathTest.php
+++ b/tests/src/Functional/UpdatePathTest.php
@@ -7,6 +7,7 @@ namespace Drupal\Tests\automatic_updates\Functional;
 use Drupal\automatic_updates\CronUpdater;
 use Drupal\automatic_updates\StatusCheckMailer;
 use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait;
 
 /**
  * @group automatic_updates
@@ -14,6 +15,8 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
  */
 class UpdatePathTest extends UpdatePathTestBase {
 
+  use AssertPreconditionsTrait;
+
   /**
    * {@inheritdoc}
    */