Skip to content
Back to archipelago.banuacoder.com

Scaffolding

test_prereq Free ​

Tier
Freeversion 1.0.7
Command
archipelago generate test_prereq
Generates
  • utilities/test_prereq

TestPrerequisite contract for composition-root test-environment setup. Implemented by packages that perform real platform I/O (e.g. @preResolve getters injectable awaits inside $initGetIt) during DI init, so initializeInjector() can run under plain flutter test without a MissingPluginException. Pure Dart, no Flutter dependency — you won't generate this directly; consuming bricks self-generate it.

Variables ​

VariableTypeDefaultDescription
isForMonorepobooleantrueWhether this is being generated as part of a monorepo

Install ​

Interactive ​

bash
archipelago generate test_prereq

Non-interactive (CI) ​

bash
archipelago generate test_prereq --config my_config.json

In practice you rarely run this yourself: app_debugger and other infrastructure bricks self-generate test_prereq as a hard dependency (via SubBrickGenerator) the first time they need it, checking ArchipelagoConfigHelper.installedBricks().contains('test_prereq') first so it only runs once.

Generated Structure ​

text
utilities/
└── test_prereq/
    └── lib/
        ├── test_prereq.dart
        └── src/
            └── test_prerequisite.dart

A single pure-Dart package — no Flutter SDK dependency, so any package can depend on it at zero cost.

Public API ​

dart
abstract interface class TestPrerequisite {
  String get owner;               // package name, for diagnostics
  String get reason;               // mirrors the declaring pubspec's `reason:` field
  Future<void> install();          // idempotent — safe to call more than once
  Future<void> uninstall() async {} // default no-op; override if reversible
}

A consuming package implements this to install a platform-interface fake (or a mock initial value) that lets its real DI-init-time I/O succeed under test, without touching production behaviour. app_debugger's AppDebuggerTestPrerequisite (ships in app_debugger's own package, not here) is the reference implementation — it fakes PathProviderPlatform so PainterFlagsCubit/FabPositionCubit's @preResolve getTemporaryDirectory() calls don't throw under plain flutter test.

Invariants ​

  1. Idempotent — install() may run more than once per test process (multiple test files, setUpAll per group) and must no-op after the first call.
  2. No GetIt access — a prerequisite runs before initializeInjector(); it must not read from or write to the container.
  3. No flutter_test import — keeps it usable from integration_test/ and flutter_test_config.dart, and keeps flutter_test out of a shipped package's dependency graph.
  4. Binding-safe — may assume TestWidgetsFlutterBinding.ensureInitialized() already ran; must not call it itself.

Declaring a Prerequisite ​

A package needs one when its DI init does platform I/O. That is a @preResolve that calls a plugin, a global module init() that eagerly resolves a plugin-backed type, or an eagerly built object whose constructor touches a plugin. In the Archipelago repository, CI runs scripts/check_di_test_prerequisites and fails when a package with a @preResolve and a plugin dependency has no declaration.

A package that needs one declares it under archipelago: in its own pubspec.yaml:

yaml
archipelago:
  test_prerequisites:
    - class: AppDebuggerTestPrerequisite
      reason: >-
        AppDebuggerRegisterModule's @preResolve cubits call path_provider
        getTemporaryDirectory() during $initGetIt.

When the package's brick is installed, its post_gen hook adds the prerequisite to the app's harness. It uses shared_hooks TestHarnessWriter, which ServiceLocatorUpdater.addExternalModule calls. After a hand edit, run dart run monorepo_toolkit generate test-harness (see monorepo_toolkit). Both write every declared prerequisite of the app's dependencies into test/support/composition_root_harness.dart, in resolved order, with each reason: as a comment. test/di/injector_test.dart calls installCompositionRootPrerequisites() from that file before initializeInjector(). Commit the harness. See Testing.

DI & Wiring ​

None — TestPrerequisite implementations are plain Dart classes assembled by the generator into a test harness, not registered in the runtime DI container (invariant 2 above forbids it).

Dependencies ​

Dev-only: test ^1.31.0, very_good_analysis. No runtime dependencies.

Platform Setup ​

None — pure Dart package.

Implementations ​

Concrete fakes live in the packages that implement TestPrerequisite, each declared in its own pubspec.yaml:

Package (brick)ClassFakes
app_debugger (page)AppDebuggerTestPrerequisitePathProviderPlatform
package_info_provider (page)PackageInfoProviderTestPrerequisitePackageInfo.setMockInitialValues
device_info_impl (page)DeviceInfoTestPrerequisiteDeviceInfoPlatform (macOS and Linux hosts)
storage_sdk_shared_preferences (page)StorageSdkSharedPreferencesTestPrerequisiteSharedPreferencesAsyncPlatform (in memory)
app_blackbox (page)AppBlackboxTestPrerequisitePathProviderPlatform
app_debugger_network (page)AppDebuggerNetworkTestPrerequisiteFlutterLocalNotificationsPlatform (Android)

Several packages fake the same plugin (app_debugger and app_blackbox both fake path_provider). A plugin has one global platform instance, so the prerequisite that installs last wins. For that reason, a fake must answer every method that any package might call, not only the method its own package calls.

monitoring_impl was in the original list but needs none: it skips a vendor that fails to build, such as Crashlytics with no Firebase app (details).

Limitations ​

  • Ships with the contract only, no concrete fakes (see Implementations).
  • utilities_sdk's device_info_impl does platform I/O in a @preResolve but has no prerequisite yet. A generated app with device info fails its composition-root test until that is fixed.
  • isForMonorepo: false (standalone) skips workspace registration entirely — there is no non-monorepo consumption path documented beyond that.

Changelog ​

Run archipelago brick info test_prereq to see the latest published version. Enterprise customers can read the full CHANGELOG.md in the brick source repository.