Commit bc1ef61ac3c for woocommerce

commit bc1ef61ac3c981b19fda0712753a57ecfa22942b
Author: Chris Lilitsas <1105590+xristos3490@users.noreply.github.com>
Date:   Wed Sep 9 11:57:59 2026 +0300

    Stop force-enabling stock notifications in the PHPUnit bootstrap (#68491)

    test: stop force-enabling stock notifications feature in test bootstrap

    Claude-Session: https://claude.ai/code/session_01DZJfdgi3cz6zNKc3scahpi

diff --git a/plugins/woocommerce/changelog/wooplug-7704-stock-notifications-tests-off-by-default b/plugins/woocommerce/changelog/wooplug-7704-stock-notifications-tests-off-by-default
new file mode 100644
index 00000000000..6c32be2588a
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooplug-7704-stock-notifications-tests-off-by-default
@@ -0,0 +1,3 @@
+Significance: patch
+Type: dev
+Comment: Stop force-enabling the Back In Stock Notifications feature for the whole PHPUnit suite; affected tests now enable it themselves (no runtime change).
diff --git a/plugins/woocommerce/tests/legacy/bootstrap.php b/plugins/woocommerce/tests/legacy/bootstrap.php
index 57a8af87ef5..42f6b31ae7a 100644
--- a/plugins/woocommerce/tests/legacy/bootstrap.php
+++ b/plugins/woocommerce/tests/legacy/bootstrap.php
@@ -269,11 +269,6 @@ class WC_Unit_Tests_Bootstrap {
 		// set in time for ProductCacheController::on_init() to register its invalidation hooks.
 		update_option( 'woocommerce_feature_product_instance_caching_enabled', 'yes' );

-		// Enable Back In Stock Notifications during tests. Set here rather than in load_wc()
-		// because install_wc() includes uninstall.php, which deletes every 'woocommerce_%'
-		// option. This still runs on `setup_theme`, in time for the `init` feature check.
-		update_option( 'woocommerce_feature_customer_stock_notifications_enabled', 'yes' );
-
 		// Reload capabilities after install, see https://core.trac.wordpress.org/ticket/28374.
 		if ( version_compare( $GLOBALS['wp_version'], '4.7', '<' ) ) {
 			$GLOBALS['wp_roles']->reinit();
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/account/functions.php b/plugins/woocommerce/tests/legacy/unit-tests/account/functions.php
index f81806b99f7..587aa5a218e 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/account/functions.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/account/functions.php
@@ -73,14 +73,12 @@ class WC_Tests_Account_Functions extends WC_Unit_Test_Case {
 	public function test_wc_get_account_menu_items() {
 		$this->assertEquals(
 			array(
-				'dashboard'           => 'Dashboard',
-				'orders'              => 'Orders',
-				'downloads'           => 'Downloads',
-				// Back in Stock Notifications is enabled for the whole suite in bootstrap.php.
-				'stock-notifications' => 'Stock notifications',
-				'edit-address'        => 'Addresses',
-				'edit-account'        => 'Account details',
-				'customer-logout'     => 'Log out',
+				'dashboard'       => 'Dashboard',
+				'orders'          => 'Orders',
+				'downloads'       => 'Downloads',
+				'edit-address'    => 'Addresses',
+				'edit-account'    => 'Account details',
+				'customer-logout' => 'Log out',
 			),
 			wc_get_account_menu_items()
 		);
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/util/class-wc-tests-wc-query.php b/plugins/woocommerce/tests/legacy/unit-tests/util/class-wc-tests-wc-query.php
index bb395792cdc..37e8ea6b2af 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/util/class-wc-tests-wc-query.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/util/class-wc-tests-wc-query.php
@@ -60,8 +60,6 @@ class WC_Tests_WC_Query extends WC_Unit_Test_Case {
 			'add-payment-method'         => 'add-payment-method',
 			'delete-payment-method'      => 'delete-payment-method',
 			'set-default-payment-method' => 'set-default-payment-method',
-			// Back in Stock Notifications is enabled for the whole suite in bootstrap.php.
-			'stock-notifications'        => 'stock-notifications',
 		);
 		$this->assertEquals( $expected, $default_vars );

diff --git a/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-products-test.php b/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-products-test.php
index d44e67d03ac..f19389fc8c5 100644
--- a/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-products-test.php
+++ b/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-products-test.php
@@ -8,7 +8,6 @@
 // phpcs:ignore Squiz.Commenting.FileComment.Missing

 use Automattic\WooCommerce\Internal\ProductAttributesLookup\LookupDataStore;
-use Automattic\WooCommerce\Internal\StockNotifications\Admin\SettingsController as StockNotificationsSettings;

 require_once __DIR__ . '/class-wc-settings-unit-test-case.php';

@@ -21,11 +20,6 @@ class WC_Settings_Products_Test extends WC_Settings_Unit_Test_Case {
 	 * @testdox get_sections should get all the existing sections.
 	 */
 	public function test_get_sections() {
-		// Get customer stock notification settings.
-		// This is required because this class is loaded only in admin context,
-		// and this test doesn't run with an admin user.
-		wc_get_container()->get( StockNotificationsSettings::class );
-
 		$sut = new WC_Settings_Products();

 		$section_names = array_keys( $sut->get_sections() );
@@ -33,7 +27,6 @@ class WC_Settings_Products_Test extends WC_Settings_Unit_Test_Case {
 		$expected = array(
 			'',
 			'inventory',
-			'customer_stock_notifications',
 			'downloadable',
 		);

diff --git a/plugins/woocommerce/tests/php/src/Internal/DataStores/StockNotifications/StockNotificationsDataStoreTests.php b/plugins/woocommerce/tests/php/src/Internal/DataStores/StockNotifications/StockNotificationsDataStoreTests.php
index 3ece68a6b83..5e94eb71cf8 100644
--- a/plugins/woocommerce/tests/php/src/Internal/DataStores/StockNotifications/StockNotificationsDataStoreTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/DataStores/StockNotifications/StockNotificationsDataStoreTests.php
@@ -8,12 +8,15 @@ use Automattic\WooCommerce\Internal\DataStores\StockNotifications\StockNotificat

 use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationCancellationSource;
 use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
+use Automattic\WooCommerce\Tests\Internal\StockNotifications\StockNotificationsFeatureTrait;

 /**
  * Class StockNotificationsDataStoreTests.
  */
 class StockNotificationsDataStoreTests extends \WC_Unit_Test_Case {

+	use StockNotificationsFeatureTrait;
+
 	/**
 	 * The data store instance.
 	 *
@@ -26,6 +29,7 @@ class StockNotificationsDataStoreTests extends \WC_Unit_Test_Case {
 	 */
 	public function setUp(): void {
 		parent::setUp();
+		$this->enable_stock_notifications_feature();
 		$this->data_store = wc_get_container()->get( StockNotificationsDataStore::class );
 	}

@@ -37,6 +41,7 @@ class StockNotificationsDataStoreTests extends \WC_Unit_Test_Case {
 		global $wpdb;
 		$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_stock_notificationmeta" );
 		$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_stock_notifications" );
+		$this->restore_stock_notifications_feature_option();
 		parent::tearDown();
 	}

diff --git a/plugins/woocommerce/tests/php/src/Internal/Email/DeferredEmailQueueTest.php b/plugins/woocommerce/tests/php/src/Internal/Email/DeferredEmailQueueTest.php
index efd95b72039..136b2651c4e 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Email/DeferredEmailQueueTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Email/DeferredEmailQueueTest.php
@@ -6,12 +6,14 @@ namespace Automattic\WooCommerce\Tests\Internal\Email;
 use Automattic\WooCommerce\Internal\Email\DeferredEmailQueue;
 use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
 use Automattic\WooCommerce\Internal\StockNotifications\Notification as StockNotification;
+use Automattic\WooCommerce\Tests\Internal\StockNotifications\StockNotificationsFeatureTrait;
 use WC_Unit_Test_Case;

 /**
  * Tests for the DeferredEmailQueue class.
  */
 class DeferredEmailQueueTest extends WC_Unit_Test_Case {
+	use StockNotificationsFeatureTrait;

 	/**
 	 * The System Under Test.
@@ -32,6 +34,7 @@ class DeferredEmailQueueTest extends WC_Unit_Test_Case {
 	 */
 	public function setUp(): void {
 		parent::setUp();
+		$this->enable_stock_notifications_feature();
 		$this->sut = new DeferredEmailQueue();
 		$this->reset_queue_singleton();
 		add_filter(
@@ -55,6 +58,7 @@ class DeferredEmailQueueTest extends WC_Unit_Test_Case {
 		remove_all_actions( 'woocommerce_deferred_email_test_unsaved_product_notification' );
 		$this->set_wc_emails_deferred_queue( null );
 		$this->delete_stock_notifications();
+		$this->restore_stock_notifications_feature_option();
 		$this->reset_queue_singleton();
 		parent::tearDown();
 	}
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Admin/SettingsControllerTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Admin/SettingsControllerTests.php
index 387df931b62..28186483b89 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Admin/SettingsControllerTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Admin/SettingsControllerTests.php
@@ -13,15 +13,50 @@ use WC_Settings_Products;
  */
 class SettingsControllerTests extends \WC_Settings_Unit_Test_Case {

+	/**
+	 * The controller whose hooks the tests exercise.
+	 *
+	 * @var StockNotificationsSettings
+	 */
+	private $controller;
+
+	/**
+	 * Set up before each test.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+		// Only AdminManager resolves the controller, and only in admin context. Built directly
+		// rather than through the container, whose cached instance loses its hooks after the first test.
+		$this->controller = new StockNotificationsSettings();
+	}
+
+	/**
+	 * @testdox The Customer stock notifications section is added to the Products tab right after Inventory.
+	 */
+	public function test_customer_stock_notifications_section_is_added_after_inventory() {
+		$sut = new WC_Settings_Products();
+
+		// Other features hook sections in too, so assert the position rather than the full list.
+		$section_ids = array_keys( $sut->get_sections() );
+		$inventory   = array_search( 'inventory', $section_ids, true );
+
+		$this->assertNotFalse( $inventory );
+		$this->assertSame( 'customer_stock_notifications', $section_ids[ $inventory + 1 ] );
+	}
+
+	/**
+	 * @testdox The Customer stock notifications section is appended when there is no Inventory section to follow.
+	 */
+	public function test_customer_stock_notifications_section_is_appended_without_inventory() {
+		$sections = $this->controller->add_customer_stock_notifications_section( array( '' => 'General' ) );
+
+		$this->assertSame( array( '', 'customer_stock_notifications' ), array_keys( $sections ) );
+	}
+
 	/**
 	 * @testdox get_settings('customer_stock_notifications') should return all the settings for the customer stock notifications section.
 	 */
 	public function test_get_customer_stock_notifications_settings_returns_all_settings() {
-		// Get customer stock notification settings.
-		// This is required because this class is loaded only in admin context,
-		// and this test doesn't run with an admin user.
-		wc_get_container()->get( StockNotificationsSettings::class );
-
 		$sut = new WC_Settings_Products();

 		$settings              = $sut->get_settings_for_section( 'customer_stock_notifications' );
@@ -43,10 +78,6 @@ class SettingsControllerTests extends \WC_Settings_Unit_Test_Case {
 	 * @testdox The My Account endpoint setting is added to the Advanced tab, inside the account endpoints group, right after Downloads.
 	 */
 	public function test_my_account_endpoint_setting_is_added_to_the_advanced_tab() {
-		// Instantiated directly rather than through the container: the container caches the
-		// instance from the previous test, whose hooks the test case has since torn down.
-		new StockNotificationsSettings();
-
 		$sut = new WC_Settings_Advanced();

 		$settings = $sut->get_settings_for_section( '' );
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/AsyncTasks/NotificationsProcessorTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/AsyncTasks/NotificationsProcessorTests.php
index d0f05e11860..d13651bf482 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/AsyncTasks/NotificationsProcessorTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/AsyncTasks/NotificationsProcessorTests.php
@@ -13,6 +13,7 @@ use Automattic\WooCommerce\Internal\StockNotifications\Emails\EmailManager;
 use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
 use Automattic\WooCommerce\Enums\ProductStockStatus;
 use Automattic\WooCommerce\Enums\ProductStatus;
+use Automattic\WooCommerce\Tests\Internal\StockNotifications\StockNotificationsFeatureTrait;
 use WC_Product;
 use WC_Helper_Product;

@@ -21,6 +22,8 @@ use WC_Helper_Product;
  */
 class NotificationsProcessorTests extends \WC_Unit_Test_Case {

+	use StockNotificationsFeatureTrait;
+
 	/**
 	 * @var NotificationsProcessor
 	 */
@@ -31,6 +34,7 @@ class NotificationsProcessorTests extends \WC_Unit_Test_Case {
 	 */
 	public function setUp(): void {
 		parent::setUp();
+		$this->enable_stock_notifications_feature();
 		\WC()->queue()->cancel_all( JobManager::AS_JOB_SEND_STOCK_NOTIFICATIONS );

 		$eligibility_service = new EligibilityService();
@@ -51,6 +55,7 @@ class NotificationsProcessorTests extends \WC_Unit_Test_Case {
 		global $wpdb;
 		$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_stock_notificationmeta" );
 		$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_stock_notifications" );
+		$this->restore_stock_notifications_feature_option();
 		parent::tearDown();
 	}

diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/DataRetentionControllerTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/DataRetentionControllerTests.php
index 69bacc186cd..4d45602517b 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/DataRetentionControllerTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/DataRetentionControllerTests.php
@@ -13,6 +13,8 @@ use Automattic\WooCommerce\Internal\StockNotifications\NotificationQuery;
  */
 class DataRetentionControllerTests extends \WC_Unit_Test_Case {

+	use StockNotificationsFeatureTrait;
+
 	/**
 	 * The controller instance.
 	 *
@@ -25,6 +27,7 @@ class DataRetentionControllerTests extends \WC_Unit_Test_Case {
 	 */
 	public function setUp(): void {
 		parent::setUp();
+		$this->enable_stock_notifications_feature();
 		$this->controller = new DataRetentionController();
 	}

@@ -32,9 +35,10 @@ class DataRetentionControllerTests extends \WC_Unit_Test_Case {
 	 * Clean up after tests
 	 */
 	public function tearDown(): void {
-		parent::tearDown();
 		$this->controller->clear_daily_task();
 		delete_option( 'woocommerce_customer_stock_notifications_unverified_deletions_days_threshold' );
+		$this->restore_stock_notifications_feature_option();
+		parent::tearDown();
 	}

 	/**
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Emails/EmailActionControllerTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Emails/EmailActionControllerTests.php
index 42498aed3c7..1ec972d4a8f 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Emails/EmailActionControllerTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Emails/EmailActionControllerTests.php
@@ -9,6 +9,7 @@ use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationCancell
 use Automattic\WooCommerce\Internal\StockNotifications\Notification;
 use Automattic\WooCommerce\Internal\StockNotifications\Factory;
 use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
+use Automattic\WooCommerce\Tests\Internal\StockNotifications\StockNotificationsFeatureTrait;
 use WC_Helper_Product;

 /**
@@ -16,6 +17,8 @@ use WC_Helper_Product;
  */
 class EmailActionControllerTests extends \WC_Unit_Test_Case {

+	use StockNotificationsFeatureTrait;
+
 	/**
 	 * The System Under Test.
 	 *
@@ -35,6 +38,7 @@ class EmailActionControllerTests extends \WC_Unit_Test_Case {
 	 */
 	public function setUp(): void {
 		parent::setUp();
+		$this->enable_stock_notifications_feature();

 		// Intercept redirects so headers aren't emitted, and throw so the trailing `exit;`
 		// in production code never runs during the test.
@@ -50,6 +54,7 @@ class EmailActionControllerTests extends \WC_Unit_Test_Case {
 	 */
 	public function tearDown(): void {
 		remove_filter( 'wp_redirect', array( $this, 'intercept_redirect' ) );
+		$this->restore_stock_notifications_feature_option();
 		parent::tearDown();
 	}

diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Emails/EmailManagerTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Emails/EmailManagerTests.php
index 64a624effbb..9c3dca42b07 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Emails/EmailManagerTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Emails/EmailManagerTests.php
@@ -6,6 +6,7 @@ namespace Automattic\WooCommerce\Tests\Internal\StockNotifications\Emails;
 use Automattic\WooCommerce\Internal\StockNotifications\Emails\EmailManager;
 use Automattic\WooCommerce\Internal\StockNotifications\Notification;
 use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
+use Automattic\WooCommerce\Tests\Internal\StockNotifications\StockNotificationsFeatureTrait;
 use WC_Helper_Product;

 /**
@@ -13,6 +14,8 @@ use WC_Helper_Product;
  */
 class EmailManagerTests extends \WC_Unit_Test_Case {

+	use StockNotificationsFeatureTrait;
+
 	/**
 	 * The System Under Test.
 	 *
@@ -32,6 +35,7 @@ class EmailManagerTests extends \WC_Unit_Test_Case {
 	 */
 	public function setUp(): void {
 		parent::setUp();
+		$this->enable_stock_notifications_feature();

 		// Short-circuit `wp_mail()` so the tests never attempt a real SMTP handoff.
 		// Returning a non-null value from `pre_wp_mail` signals WP core to skip the actual send.
@@ -40,7 +44,11 @@ class EmailManagerTests extends \WC_Unit_Test_Case {
 		$this->sut = new EmailManager();
 		$this->sut->init();

-		// Boot the mailer so email classes are registered.
+		// `WC_Emails` is a singleton that keeps whichever email list it built on its first
+		// construction for the rest of the process. Reset it so this test's registration
+		// via `woocommerce_email_classes` is actually picked up rather than an earlier
+		// test's (feature-disabled) build of the list.
+		$this->reset_email_singleton();
 		WC()->mailer();
 	}

@@ -50,9 +58,22 @@ class EmailManagerTests extends \WC_Unit_Test_Case {
 	public function tearDown(): void {
 		remove_filter( 'pre_wp_mail', array( $this, 'capture_pre_wp_mail' ), 10 );
 		$this->sent_to = array();
+		$this->restore_stock_notifications_feature_option();
+		// Discard this test's `WC_Emails` build so later tests rebuild their own from
+		// whatever filters are active for them, rather than inheriting this test's list.
+		$this->reset_email_singleton();
 		parent::tearDown();
 	}

+	/**
+	 * Reset the `WC_Emails` singleton so the next `WC()->mailer()` call rebuilds it.
+	 */
+	private function reset_email_singleton(): void {
+		$instance_property = ( new \ReflectionClass( \WC_Emails::class ) )->getProperty( 'instance' );
+		$instance_property->setAccessible( true );
+		$instance_property->setValue( null, null );
+	}
+
 	/**
 	 * `pre_wp_mail` filter: record the recipient and short-circuit the actual send.
 	 *
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/FactoryTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/FactoryTests.php
index 41a98a0b818..8889b6fc6e0 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/FactoryTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/FactoryTests.php
@@ -11,6 +11,24 @@ use Automattic\WooCommerce\Internal\StockNotifications\Factory;
  */
 class FactoryTests extends \WC_Unit_Test_Case {

+	use StockNotificationsFeatureTrait;
+
+	/**
+	 * Set up the test.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+		$this->enable_stock_notifications_feature();
+	}
+
+	/**
+	 * Tear down the test.
+	 */
+	public function tearDown(): void {
+		$this->restore_stock_notifications_feature_option();
+		parent::tearDown();
+	}
+
 	/**
 	 * Test the factory.
 	 */
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/MyAccountEndpointTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/MyAccountEndpointTests.php
index 1da3f8c1269..449fc83b4b3 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/MyAccountEndpointTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/MyAccountEndpointTests.php
@@ -12,6 +12,7 @@ use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
 use Automattic\WooCommerce\Internal\StockNotifications\Factory;
 use Automattic\WooCommerce\Internal\StockNotifications\Frontend\MyAccountEndpoint;
 use Automattic\WooCommerce\Internal\StockNotifications\Notification;
+use Automattic\WooCommerce\Tests\Internal\StockNotifications\StockNotificationsFeatureTrait;
 use WC_Helper_Product;

 /**
@@ -19,6 +20,8 @@ use WC_Helper_Product;
  */
 class MyAccountEndpointTests extends \WC_Unit_Test_Case {

+	use StockNotificationsFeatureTrait;
+
 	/**
 	 * Location passed to the last suppressed redirect, or null if none happened.
 	 *
@@ -31,6 +34,7 @@ class MyAccountEndpointTests extends \WC_Unit_Test_Case {
 	 */
 	public function setUp(): void {
 		parent::setUp();
+		$this->enable_stock_notifications_feature();
 		\wc_clear_notices();
 		$this->redirect_location = null;
 		add_filter( 'wp_redirect', array( $this, 'capture_redirect' ) );
@@ -51,6 +55,7 @@ class MyAccountEndpointTests extends \WC_Unit_Test_Case {
 		if ( isset( $wp->query_vars[ MyAccountEndpoint::ENDPOINT ] ) ) {
 			unset( $wp->query_vars[ MyAccountEndpoint::ENDPOINT ] );
 		}
+		$this->restore_stock_notifications_feature_option();
 		parent::tearDown();
 	}

diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/NotificationManagementServiceTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/NotificationManagementServiceTests.php
index 58d04e63316..8bee8865016 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/NotificationManagementServiceTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/NotificationManagementServiceTests.php
@@ -8,6 +8,7 @@ use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
 use Automattic\WooCommerce\Internal\StockNotifications\Factory;
 use Automattic\WooCommerce\Internal\StockNotifications\Frontend\NotificationManagementService;
 use Automattic\WooCommerce\Internal\StockNotifications\Notification;
+use Automattic\WooCommerce\Tests\Internal\StockNotifications\StockNotificationsFeatureTrait;
 use WC_Helper_Product;

 /**
@@ -15,6 +16,8 @@ use WC_Helper_Product;
  */
 class NotificationManagementServiceTests extends \WC_Unit_Test_Case {

+	use StockNotificationsFeatureTrait;
+
 	/**
 	 * The System Under Test.
 	 *
@@ -34,6 +37,7 @@ class NotificationManagementServiceTests extends \WC_Unit_Test_Case {
 	 */
 	public function setUp(): void {
 		parent::setUp();
+		$this->enable_stock_notifications_feature();

 		// Intercept redirects so headers aren't emitted, and throw so the trailing `exit;`
 		// in production code never runs during the test.
@@ -62,6 +66,7 @@ class NotificationManagementServiceTests extends \WC_Unit_Test_Case {
 		$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_stock_notificationmeta" );
 		$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_stock_notifications" );

+		$this->restore_stock_notifications_feature_option();
 		parent::tearDown();
 	}

diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/SignupServiceTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/SignupServiceTests.php
index 768aff45a06..1a339a5beba 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/SignupServiceTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/SignupServiceTests.php
@@ -10,6 +10,7 @@ use Automattic\WooCommerce\Internal\StockNotifications\Frontend\SignupService;
 use Automattic\WooCommerce\Internal\StockNotifications\Notification;
 use Automattic\WooCommerce\Internal\StockNotifications\Utilities\EligibilityService;
 use Automattic\WooCommerce\Internal\StockNotifications\Utilities\StockManagementHelper;
+use Automattic\WooCommerce\Tests\Internal\StockNotifications\StockNotificationsFeatureTrait;
 use WC_Helper_Product;

 /**
@@ -17,6 +18,8 @@ use WC_Helper_Product;
  */
 class SignupServiceTests extends \WC_Unit_Test_Case {

+	use StockNotificationsFeatureTrait;
+
 	/**
 	 * The System Under Test.
 	 *
@@ -36,6 +39,7 @@ class SignupServiceTests extends \WC_Unit_Test_Case {
 	 */
 	public function setUp(): void {
 		parent::setUp();
+		$this->enable_stock_notifications_feature();

 		update_option( 'woocommerce_customer_stock_notifications_allow_signups', 'yes' );

@@ -64,6 +68,7 @@ class SignupServiceTests extends \WC_Unit_Test_Case {
 		$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_stock_notificationmeta" );
 		$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_stock_notifications" );

+		$this->restore_stock_notifications_feature_option();
 		parent::tearDown();
 	}

diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/NotificationTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/NotificationTests.php
index 8a275f99eec..1111bc23674 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/NotificationTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/NotificationTests.php
@@ -11,6 +11,16 @@ use Automattic\WooCommerce\Internal\StockNotifications\Config;
  */
 class NotificationTests extends \WC_Unit_Test_Case {

+	use StockNotificationsFeatureTrait;
+
+	/**
+	 * Set up the test.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+		$this->enable_stock_notifications_feature();
+	}
+
 	/**
 	 * @after
 	 */
@@ -19,6 +29,7 @@ class NotificationTests extends \WC_Unit_Test_Case {
 		global $wpdb;
 		$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_stock_notificationmeta" );
 		$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_stock_notifications" );
+		$this->restore_stock_notifications_feature_option();
 		parent::tearDown();
 	}

diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Privacy/PrivacyEraserTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Privacy/PrivacyEraserTests.php
index 67f94e67b8e..31a28b3b6f8 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Privacy/PrivacyEraserTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Privacy/PrivacyEraserTests.php
@@ -6,11 +6,31 @@ namespace Automattic\WooCommerce\Tests\Internal\StockNotifications\Privacy;
 use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
 use Automattic\WooCommerce\Internal\StockNotifications\Privacy\PrivacyEraser;
 use Automattic\WooCommerce\Internal\StockNotifications\Notification;
+use Automattic\WooCommerce\Tests\Internal\StockNotifications\StockNotificationsFeatureTrait;

 /**
  * PrivacyEraser tests.
  */
 class PrivacyEraserTests extends \WC_Unit_Test_Case {
+
+	use StockNotificationsFeatureTrait;
+
+	/**
+	 * Set up the test.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+		$this->enable_stock_notifications_feature();
+	}
+
+	/**
+	 * Tear down the test.
+	 */
+	public function tearDown(): void {
+		$this->restore_stock_notifications_feature_option();
+		parent::tearDown();
+	}
+
 	/**
 	 * Test that privacy eraser makes notification data anonymous.
 	 */
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsFeatureTrait.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsFeatureTrait.php
new file mode 100644
index 00000000000..f728e06a4ba
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsFeatureTrait.php
@@ -0,0 +1,61 @@
+<?php
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Tests\Internal\StockNotifications;
+
+use Automattic\WooCommerce\Internal\StockNotifications\StockNotifications;
+
+/**
+ * Enables the Back In Stock Notifications feature for a test's lifetime.
+ *
+ * The feature is off by default in the test suite (see WOOPLUG-7704), so any
+ * test that reads or writes a `Notification` must enable it itself, following
+ * the Order Withdrawal set_up()/tear_down() pattern. Tests build their own
+ * subject under test; only tests of the wiring itself call
+ * `init_stock_notifications_services()`.
+ */
+trait StockNotificationsFeatureTrait {
+
+	/**
+	 * The feature option's value before the test enabled it.
+	 *
+	 * @var mixed
+	 */
+	private $stock_notifications_original_feature_option;
+
+	/**
+	 * Enable the feature and register its data store, enough to read and write a Notification.
+	 *
+	 * Deliberately stops short of `maybe_init_services()`: hooking the container's
+	 * services would run real instances alongside the mocks a test injects into its own.
+	 */
+	private function enable_stock_notifications_feature(): void {
+		$this->stock_notifications_original_feature_option = get_option( StockNotifications::ENABLE_OPTION_NAME, false );
+		update_option( StockNotifications::ENABLE_OPTION_NAME, 'yes' );
+		add_filter( 'woocommerce_data_stores', array( wc_get_container()->get( StockNotifications::class ), 'register_data_stores' ) );
+	}
+
+	/**
+	 * Wire up every service `maybe_init_services()` gates. Only for tests of the wiring itself.
+	 *
+	 * Resets the container first: it caches each service, and the hooks a cached instance
+	 * added in an earlier test are gone once that test tore down, so only a fresh
+	 * resolution actually re-hooks.
+	 */
+	private function init_stock_notifications_services(): void {
+		$container = wc_get_container();
+		$container->reset_all_resolved();
+		$container->get( StockNotifications::class )->maybe_init_services();
+	}
+
+	/**
+	 * Restore the feature option to its pre-test value.
+	 */
+	private function restore_stock_notifications_feature_option(): void {
+		if ( false === $this->stock_notifications_original_feature_option ) {
+			delete_option( StockNotifications::ENABLE_OPTION_NAME );
+		} else {
+			update_option( StockNotifications::ENABLE_OPTION_NAME, $this->stock_notifications_original_feature_option );
+		}
+	}
+}
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsTests.php
index b4793cfcba5..f2bebdf100b 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsTests.php
@@ -11,6 +11,15 @@ use Automattic\WooCommerce\Internal\StockNotifications\StockNotifications;
  * StockNotifications controller tests.
  */
 class StockNotificationsTests extends \WC_Unit_Test_Case {
+	use StockNotificationsFeatureTrait;
+
+	/**
+	 * Set up before each test.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+		$this->enable_stock_notifications_feature();
+	}

 	/**
 	 * Clean up after tests.
@@ -19,6 +28,7 @@ class StockNotificationsTests extends \WC_Unit_Test_Case {
 		wc_get_container()->get( DataRetentionController::class )->clear_daily_task();
 		delete_option( 'woocommerce_customer_stock_notifications_unverified_deletions_days_threshold' );
 		delete_option( 'woocommerce_queue_flush_rewrite_rules' );
+		$this->restore_stock_notifications_feature_option();
 		parent::tearDown();
 	}

@@ -50,6 +60,7 @@ class StockNotificationsTests extends \WC_Unit_Test_Case {
 	 */
 	public function test_disabling_the_feature_clears_the_daily_task(): void {
 		update_option( 'woocommerce_customer_stock_notifications_unverified_deletions_days_threshold', 30 );
+		$this->fire_feature_changed( true );
 		$this->assertSame( 'daily', wp_get_schedule( DataRetentionController::DAILY_TASK_HOOK ) );

 		$this->fire_feature_changed( false );
@@ -79,6 +90,7 @@ class StockNotificationsTests extends \WC_Unit_Test_Case {
 	 */
 	public function test_other_features_are_ignored(): void {
 		update_option( 'woocommerce_customer_stock_notifications_unverified_deletions_days_threshold', 30 );
+		$this->fire_feature_changed( true );
 		delete_option( 'woocommerce_queue_flush_rewrite_rules' );

 		$this->fire_feature_changed( false, 'some_other_feature' );
@@ -86,4 +98,55 @@ class StockNotificationsTests extends \WC_Unit_Test_Case {
 		$this->assertSame( 'daily', wp_get_schedule( DataRetentionController::DAILY_TASK_HOOK ) );
 		$this->assertFalse( get_option( 'woocommerce_queue_flush_rewrite_rules' ) );
 	}
+
+	/**
+	 * @testdox Enabling the feature wires the hooks of container-resolved services.
+	 */
+	public function test_enabling_the_feature_wires_container_resolved_services(): void {
+		$this->init_stock_notifications_services();
+
+		$this->assertArrayHasKey( 'stock-notifications', wc_get_account_menu_items() );
+	}
+
+	/**
+	 * @testdox The stock notification data store is not registered while the feature is disabled.
+	 */
+	public function test_maybe_init_services_does_nothing_when_the_feature_is_disabled(): void {
+		$controller = wc_get_container()->get( StockNotifications::class );
+
+		// Undo what setUp() wired up, then re-run with the feature off.
+		remove_filter( 'woocommerce_data_stores', array( $controller, 'register_data_stores' ) );
+		update_option( StockNotifications::ENABLE_OPTION_NAME, 'no' );
+		$controller->maybe_init_services();
+
+		$this->expectException( \Exception::class );
+		$this->expectExceptionMessage( 'Invalid data store.' );
+
+		new \WC_Data_Store( 'stock_notification' );
+	}
+
+	/**
+	 * @testdox register_data_stores re-checks the option, so a hooked callback stays inert once the feature is turned off.
+	 */
+	public function test_register_data_stores_ignores_a_hooked_callback_once_the_feature_is_disabled(): void {
+		$controller = wc_get_container()->get( StockNotifications::class );
+		$this->assertNotFalse( has_filter( 'woocommerce_data_stores', array( $controller, 'register_data_stores' ) ) );
+
+		update_option( StockNotifications::ENABLE_OPTION_NAME, 'no' );
+
+		$stores = array( 'product' => 'WC_Product_Data_Store_CPT' );
+
+		$this->assertSame( $stores, $controller->register_data_stores( $stores ) );
+	}
+
+	/**
+	 * @testdox The stock notification data store becomes available once the feature is enabled.
+	 */
+	public function test_maybe_init_services_registers_the_data_store_when_the_feature_is_enabled(): void {
+		$this->init_stock_notifications_services();
+
+		$store = new \WC_Data_Store( 'stock_notification' );
+
+		$this->assertSame( \Automattic\WooCommerce\Internal\DataStores\StockNotifications\StockNotificationsDataStore::class, $store->get_current_class_name() );
+	}
 }
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockSyncControllerTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockSyncControllerTests.php
index 29e307daab6..9ee7e6f783b 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockSyncControllerTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockSyncControllerTests.php
@@ -16,6 +16,8 @@ use Automattic\WooCommerce\Internal\StockNotifications\AsyncTasks\JobManager;
  */
 class StockSyncControllerTests extends \WC_Unit_Test_Case {

+	use StockNotificationsFeatureTrait;
+
 	/**
 	 * @var StockSyncController
 	 */
@@ -26,6 +28,7 @@ class StockSyncControllerTests extends \WC_Unit_Test_Case {
 	 */
 	public function setUp(): void {
 		parent::setUp();
+		$this->enable_stock_notifications_feature();
 		$this->sut           = new StockSyncController();
 		$eligibility_service = new EligibilityService();
 		$eligibility_service->init( new StockManagementHelper() );
@@ -37,6 +40,7 @@ class StockSyncControllerTests extends \WC_Unit_Test_Case {
 	 * Tear down the test.
 	 */
 	public function tearDown(): void {
+		$this->restore_stock_notifications_feature_option();
 		parent::tearDown();
 		unset( $this->sut );
 	}
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Utilities/EligibilityServiceTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Utilities/EligibilityServiceTests.php
index ad54e7e2d22..5236f6e4dc3 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Utilities/EligibilityServiceTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Utilities/EligibilityServiceTests.php
@@ -8,6 +8,7 @@ use Automattic\WooCommerce\Internal\StockNotifications\Utilities\StockManagement
 use Automattic\WooCommerce\Internal\StockNotifications\Notification;
 use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
 use Automattic\WooCommerce\Enums\ProductStockStatus;
+use Automattic\WooCommerce\Tests\Internal\StockNotifications\StockNotificationsFeatureTrait;
 use WC_Helper_Product;

 /**
@@ -15,6 +16,8 @@ use WC_Helper_Product;
  */
 class EligibilityServiceTests extends \WC_Unit_Test_Case {

+	use StockNotificationsFeatureTrait;
+
 	/**
 	 * @var EligibilityService
 	 */
@@ -25,6 +28,7 @@ class EligibilityServiceTests extends \WC_Unit_Test_Case {
 	 */
 	public function setUp(): void {
 		parent::setUp();
+		$this->enable_stock_notifications_feature();
 		$stock_management_helper = new StockManagementHelper();
 		$this->sut               = new EligibilityService();
 		$this->sut->init( $stock_management_helper );
@@ -39,6 +43,7 @@ class EligibilityServiceTests extends \WC_Unit_Test_Case {
 		global $wpdb;
 		$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_stock_notificationmeta" );
 		$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_stock_notifications" );
+		$this->restore_stock_notifications_feature_option();
 		parent::tearDown();
 	}