Skip to content

Seed the test's environment before its modules apply - #54

Merged
ipjohnson merged 1 commit into
mainfrom
test-environment-before-modules
Sep 1, 2026
Merged

ipjohnson merged 1 commit into
mainfrom
test-environment-before-modules

Conversation

@ipjohnson

Copy link
Copy Markdown
Owner

Module registrations are conditioned as they are applied: LoadModules answers [IfEnvironment] from the IModuleEnvironment already in the collection, or a process default when there is none. Both test runners loaded modules before the service-setup pass. An environment a setup attribute registered therefore arrived after every condition had been decided against the default, and a test had no way to put itself under the environment it declares.

Hardened's second framework trial hit exactly this (finding D-B-10): [EnvironmentName] on a test method against an [IfEnvironment]-gated registration, driven through the real xunit v3 entry point, and the gated registration never applied. The Hardened-side repro fails against 1.2.2 and passes against this branch, verified end to end with these packages packed locally.

The fix is a new hook rather than a reorder. Running the service-setup pass before the modules would invert the documented contract that a test registration beats an application one, so the ordering stays and the environment gets its own, earlier step:

  • IModuleEnvironmentProvider in Runtime.Interfaces, beside IDependencyModuleProvider: an attribute names the environment for a test method, or returns null to defer to a wider scope.
  • Both runners consult it between the test-case info and the modules, widest scope first, so the narrowest attribute that answers decides.
  • It takes a MethodInfo rather than a test-method context because it returns a Runtime type, and Testing deliberately does not reference Runtime - the same boundary the duplicated module loading in each runner exists to respect.

Tests: xUnit and NUnit integ tests drive the seeding through the real runners - a [SingletonService] + [IfEnvironment] registration applies under the seeded environment and stays absent without it, an environment-reading module sees the seeded instance, and the method-level seed beats the class-level one. The Runtime API snapshot gains the interface. The full solution suite passes on both TFMs.

🤖 Generated with Claude Code

Module registrations are conditioned as they are applied: LoadModules
answers [IfEnvironment] from the IModuleEnvironment already in the
collection, or a process default when there is none. Both test runners
loaded modules before the service-setup pass, so an environment a setup
attribute registered arrived after every condition had been decided
against the default - a test had no way to put itself under the
environment it declares. Hardened's second framework trial hit exactly
this: [EnvironmentName] on a test method against an [IfEnvironment]
module, and the gated registration never applied.

IModuleEnvironmentProvider is the new hook: an attribute names the
environment, both runners consult it before loading any module, and the
narrowest scope that answers decides. It lives in Runtime.Interfaces
beside IDependencyModuleProvider and takes a MethodInfo rather than a
test-method context, because it returns a Runtime type and Testing
deliberately does not reference Runtime.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ipjohnson
ipjohnson merged commit e0def05 into main Sep 1, 2026
2 checks passed
ipjohnson added a commit that referenced this pull request Sep 1, 2026
Version and changelog for #54. A minor version rather than a patch,
because IModuleEnvironmentProvider is new public API on the Runtime
package. FileVersion catches up two releases: the 1.2.3 cut moved
VersionPrefix and left it at 1.2.2.0, though its own comment says it
carries the real version for anyone inspecting the binary.

Co-authored-by: Ian Johnson <ian.phillip.johnson@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant