Skip to content

FAQ

Questions that come up repeatedly but don't map to a single diagnostic code or error message.

Why does a construction cycle fail instead of just omitting the cycling member?

AutoFixture (and similar tools) default to throwing on a self-referencing object graph, but some configurations opt into silently omitting the cycling member instead (AutoFixture's OmitOnRecursionBehavior). Compono has no equivalent opt-out — a genuine construction cycle always fails fast, immediately, with a path-annotated CompositionException (see Common Errors: Runtime Composition Failures).

This is deliberate: an omitted member is a silently incomplete object — the test gets a value back, but part of its graph is missing without any signal that happened. Compono's explicit-over-implicit design principle (the same one behind Compono.NSubstitute's no-auto-configuration default) treats that as worse than a loud failure with a clear, reproducible seed. If your object graph is genuinely self-referencing, break the cycle explicitly with a Register<T> factory that supplies the recursive member directly, rather than relying on generated default construction to handle it.

Why is a substitute's method returning null/default instead of a sensible value?

Compono.NSubstitute never auto-configures a substitute's members — see Compono.NSubstitute's Package Guide. Stub the specific member you depend on explicitly.

Why can't I compose HttpClient (or another BCL type) directly?

HttpClient has multiple accessible constructors, so composing it directly fails with CMP0001 — see Reference: Diagnostics — CMP0001. The general workaround is to compose an interface wrapper around the BCL type instead of the type itself — see Migrating from AutoFixture for a real, complete example (IHttpClientProvider).

Can I stack [Compose] with [Compose<TProfile>] on the same test method?

No — only one Compose-family attribute per method is supported; stacking throws a CompositionException at data-binding time. See Compono.XunitV3's Package Guide.

Why did two composed values of the same type turn out identical when I expected them different (or vice versa)?

Composition is independent by default — two parameters of the same type get two separate composed instances, even if they look alike. [Shared] opts a specific type into being reused across a test row instead — see Shared Values. If you're seeing unexpected sameness without using [Shared], check whether a profile registered a singleton-style factory (Register<T> that captures and returns the same instance every call) rather than a fresh one.

Is there a stable 1.0 release yet?

Not yet — every published version during public preview is a 0.x.y-preview.N prerelease (--prerelease required to install). See MVP Non-goals and ADR-0031 for the compatibility policy this implies: a breaking change bumps the minor version (0.X+1.0), not a major version, until 1.0.

Next