Compono MVP¶
Objective¶
The MVP should prove that Compono can provide a coherent, fast, source-generated test composition experience across:
- Core object composition
- xUnit v3
- NSubstitute
- Bogus
The MVP is not an AutoFixture migration layer and does not aim for feature parity.
Success Criteria¶
The MVP succeeds when:
- A developer can compose typical modern .NET object graphs without runtime constructor reflection on the generated path.
- An xUnit v3 theory can declare composed parameters.
- A shared test-double parameter is injected into the system under test.
- Bogus can provide deterministic semantic values through an ancillary package.
- A failure produces a readable dependency path and reproducible seed.
- One real test project can be rewritten to use Compono and remains pleasant to maintain.
- The core package has no dependencies on test frameworks, mocking frameworks, or Bogus.
MVP Package Set¶
Compono.Generators may be shipped as a transitive analyzer dependency rather than a package users reference directly.
Milestone 0: Product and Design Contract¶
Deliverables¶
- Compono Manifesto
- Architecture document
- Public API design document
- MVP document
- Initial architecture decision records
- 20–30 desired usage examples
- Initial package dependency diagram
Exit Criteria¶
- Core terminology is stable enough to begin implementation
- Open questions are explicitly recorded
- Representative examples cover all MVP packages
Milestone 1: Source-Generation Foundation¶
Scope¶
- Incremental source generator
- Discovery of constructible source types
- Constructor selection prototype
- Generated direct constructor invocation
- Generated request metadata
- Plan registration mechanism
- Compile-time diagnostics for unsupported or ambiguous construction
- Benchmark harness comparing generated construction with reflection baselines
GitHub Issue Themes¶
- Create generator project
- Define generated-plan contract
- Discover constructors
- Generate plan registration
- Emit required-member assignments
- Emit nullability metadata
- Add generator snapshot tests
- Add benchmark project
Exit Criteria¶
uses a generated plan for a representative record or class.
Milestone 2: Core Composition Engine¶
Design: ADR-0010 (request/pipeline/failure/diagnostics), ADR-0011 (scope/shared values/recursion), ADR-0012 (path identity/random forking/CreateMany seed), ADR-0013 (collections). These supersede the earlier ADR-0007/0008/0009 after a deep design review — see each ADR's Links section. Tracked by PLAN-0002.
Scope¶
CompositionContext- Composition requests and paths
- Provider pipeline
- Deterministic seed
- Forkable random source
- Built-in primitive generation
- Enum and nullable generation
- Common collection generation
- Exact registrations
- Composition scopes
- Shared values
- Recursion detection
- Structured diagnostics
Create<T>()CreateMany<T>()
Initial Built-in Types¶
stringbool- Integral numeric types
- Floating-point types
decimalGuidDateTimeDateTimeOffsetDateOnlyTimeOnlyTimeSpan- Enums
- Nullable value types
- Arrays
List<T>IReadOnlyList<T>HashSet<T>Dictionary<TKey, TValue>
This list may be reduced if implementation complexity threatens the milestone.
Exit Criteria¶
- Typical object graphs compose deterministically
- Shared instances are reused correctly
- Recursive graphs fail clearly
- Generated-plan execution is the preferred path
- Provider precedence is covered by tests
Milestone 3: Profiles and Configuration¶
Design: ADR-0017 (immutable Composer/CompositionBuilder/CompositionConfiguration split, build-time configuration validation), ADR-0018 (ICompositionProfile, profile application order, recursion/provenance), ADR-0019 (exact registrations, duplicate-registration conflicts, native IServiceProvider fallback in stage 3), ADR-0020 (type/member value rules as internal stage-4 providers, collection-size as queried configuration policy). ADR-0019 adds the ManualResolve path-segment kind (an additive extension to ADR-0012's Accepted path-identity contract, not an edit to it) and a construction-cycle guard around registration/rule factory invocation. Public provider extensibility (how NSubstitute/Bogus/custom pattern-matching logic eventually plug into stages ⅚) is deliberately deferred to Milestone 5 — see that milestone's section below. Tracked by PLAN-0003.
Scope¶
- Immutable composer configuration (
Composer.Create(builder => ...), frozenCompositionConfiguration) - Reusable profiles (
ICompositionProfile,AddProfile<T>()/AddProfile(instance), eager in-order application, cycle detection) - Integration extension registration (ordinary C# extension methods on
CompositionBuilder— no new core mechanism required) - Collection-size configuration (global default + member-scoped override, queried by stage 7, not a stage-4 rule)
- Exact type registrations (
Register<T>(Func<ICompositionContext, T>)/Register<T>(Func<T>), duplicate registration is a build-time conflict) - Native
IServiceProviderfallback (UseServiceProvider(IServiceProvider), folded into stage 3 after exact registrations) - Type/member rule prototype (
.For<T>().Use(...),.For<T>().Member(...).Use(...), exact-type matching, member rule beats type rule) - Configuration conflict diagnostics (build-time
CompositionConfigurationExceptionfor duplicate registrations/rules and profile cycles, naming every conflicting source)
Exit Criteria¶
A project can define one reusable profile and use it for both programmatic and test-framework composition.
Milestone 4: xUnit v3 Integration¶
Design: ADR-0021 (core CompositionRow/CompositionRequestKind.TestParameter entry point, the stage-2 shared-value read-gate change), ADR-0022 (Compono.XunitV3 package: [Compose]/[Compose<TProfile>]/[Shared] attributes, inline/composed binding, profile selection, seed policy, diagnostics, package dependencies), ADR-0023 (the Compono.Xunit → Compono.XunitV3 rename). Implemented across Phases 0-4 (core entry point, attribute skeleton, binding algorithm, test suites/verification, docs/cleanup) — see PLAN-0004 for the phase-by-phase account. One known gap remains open past Phase 4: an interface/abstract/ delegate-typed [Compose]-attributed parameter (including the IRepository shape in the Example below) reports CMP0003 and fails to compile even when a profile registration or an inline value would satisfy it at runtime — see PLAN-0004's Open Items.
Scope¶
- xUnit v3 data attribute
- One composition context per theory row
- Parameter request metadata
- Inline values plus composed values
- Shared parameter support
- Profile selection
- Seed reporting
- Generator support for test methods if needed
Example¶
[Theory]
[Compose<ApplicationTestProfile>]
public void Creates_service(
[Shared] IRepository repository,
OrderService service,
CreateOrder command)
{
}
This is the target shape; IRepository being interface-typed currently hits the CMP0003 gap noted above — test/Compono.XunitV3.SampleTests uses a concrete Repository type for its own [Shared] theory to route around it until that gap is resolved.
Exit Criteria¶
Met for the parameter shapes Compono.XunitV3 currently supports, verified through test/Compono.XunitV3.Tests and a real xUnit v3 runner against test/Compono.XunitV3.SampleTests (PLAN-0004 Phase 3); the CMP0003 gap above means "composed parameters work under xUnit v3" doesn't yet extend to a bare interface/abstract/delegate-typed [Compose] parameter:
- Composed parameters work under xUnit v3
- Inline values take precedence
- Shared values flow into composed systems under test
- Failure output includes a seed
Milestone 5: NSubstitute Integration¶
Owns the public provider-extensibility design deferred by Milestone 3's design review (docs/adr/0018-composition-profiles.md's Context, and the M3 design review's first fork): how an integration package contributes open-ended, pattern-matching logic (e.g. "any interface type") into pipeline stages ⅚, as opposed to the closed-set, per-type/per-member rules Milestone 3 already covers via internal Compono-authored stage-4 providers (docs/adr/0020-composition-configuration-rules.md). Deliberately not designed in Milestone 3, since it had no real consumer there.
Design: ADR-0024 (the core public provider contract — ICompositionValueProvider, registration into stages ⅚, diagnostics identity — reusable by Milestone 6 without a redesign), ADR-0025 (Compono.NSubstitute package: substitutable-shape rules including delegate types, NSubstituteOptions, diagnostics). ADR-0024's core engine extension point is implemented (PLAN-0005 Phase 0) — builder.AddSemanticProvider(...)/ builder.AddTestDoubleProvider(...) are real, tested public API today. Compono.NSubstitute itself (ADR-0025) is implemented and test-covered/ end-to-end verified (PLAN-0005 Phase 2) — NSubstituteProvider/ NSubstituteOptions/UseNSubstitute() are real, tested code, verified both by Compono.NSubstitute.Tests and by a real packaged Compono.XunitV3.SampleTests run of this milestone's own Goal scenario. See PLAN-0005 for the phase-by-phase tracker (Phase 3, docs/cleanup, still open) and ADR-0024's Amendment 2 for a Compono.Generators compile-time check (CMP0003) Phase 2's real verification found and fixed along the way — an interface/abstract-class/ delegate root is now correctly left for a provider to satisfy at runtime instead of being rejected at compile time.
Scope¶
- Test-double provider contract
- Interface substitutes
- Delegate substitutes (added during ADR-0025's design — NSubstitute supports this natively at negligible extra cost; not in this bullet list originally)
- Optional abstract-class substitutes
- Shared substitute reuse
- Integration-specific configuration
- Clear diagnostics when substitution is unsupported
Non-goals¶
- Recursive auto-configuration of substitute members
- NSubstitute API wrappers
- Pinning NSubstitute versions in the core package
Exit Criteria¶
A typical service test can receive a shared substitute, a composed system under test, and a composed request with no manual setup.
Milestone 6: Bogus Integration¶
Scope¶
- Semantic value-provider contract
- Shared deterministic seed
- Bogus
Fakeraccess - Locale configuration
- Conservative member-name conventions
- Explicit member rules
- Initial correlated-value experiment
Initial Conventions¶
Potential mappings:
FirstNameLastNameFullNameEmailPhoneNumberStreetAddressCityStatePostalCodeCompanyName
Ambiguous member names such as Name should not be guessed aggressively.
Exit Criteria¶
A composed customer can receive realistic, deterministic values without the core package referencing Bogus.
Milestone 7: Dogfooding¶
Scope¶
- Select one existing real-world project
- Rewrite its tests using Compono
- Record missing capabilities
- Measure performance
- Measure API friction
- Refine diagnostics
- Remove unnecessary abstractions
Success Measures¶
- Tests are at least as readable as before
- The composition model remains understandable
- Most setup belongs in profiles rather than custom attributes
- Failures are reproducible
- Performance does not regress unacceptably
Milestone 8: Public Preview¶
Scope¶
- Publish
0.xpackages - README and getting-started guide
- Architecture documentation
- Samples
- Versioning policy
- Contribution guidance
- Issue templates
- Benchmark results
- Explicit known limitations
MVP Non-goals¶
- AutoFixture API compatibility
- AutoFixture migration tooling
- NUnit or MSTest support
- Moq or FakeItEasy support
- Native AOT certification
- Full reflection fallback
- Open generic registrations
- Source-generated test methods beyond what xUnit requires
- Analyzers beyond generator diagnostics
- Property-based testing
- Snapshot testing
- Database seeding
- Every collection type
- Every Bogus dataset
- Global mutable configuration
- Runtime plugin discovery
- Stable 1.0 API
Open Decisions Before Implementation¶
Runtime reflection policy— default direction resolved by ADR-0001; the exact opt-in mechanism for a future compatibility mode is still open.Exact public root type name—Composer, settled by Milestone 3 Phase 0's shippedComposer.Create()/Composer.Create(Action<CompositionBuilder>).Attribute names— resolved by ADR-0022:[Compose],[Compose<TProfile>],[Shared].Shared-value matching rules— type-based only for Milestone 2, resolved by ADR-0011; confirmed type-based only for Milestone 4's[Shared]too, by ADR-0022 — name/qualifier-based sharing remains deferred past Milestone 4, with no consumer yet.Sync or async provider APIs— resolved by ADR-0010.Constructor selection algorithm— resolved by ADR-0002.- Required-member population rules
- Nullability generation defaults
Generator package distribution— resolved by ADR-0003.Deterministic output compatibility guarantees— resolved by ADR-0012.
Suggested Initial GitHub Epics¶
- Product design and ADRs
- Source generator foundation
- Core context and provider pipeline
- Deterministic value generation
- Object graph composition
- Profiles and configuration
- xUnit v3 integration
- NSubstitute integration
- Bogus integration
- Diagnostics
- Benchmarks
- Dogfooding and public preview