An honest comparison, not a sales pitch. These tools solve overlapping but genuinely different problems; several of them are more mature, more widely used, and better at what they do than XFTY is at the same narrow task. Where XFTY has a real edge, it's a narrow one: generating a related, constraint-valid object graph, with explicit control over whether and how it gets persisted — not filling in one object's properties.
This is a comparison, not a rivalry: three of the four tools below don't
just compete with XFTY, they now pair with it. Xfty.Bogus,
Xfty.AutoFixture, and Xfty.AutoBogus are separate, opt-in packages that
let a single test use XFTY for the graph-shaped work it's actually good at
(relationships, shared ancestors, insert modes) and Bogus/AutoFixture/
AutoBogus for whichever piece of their job XFTY doesn't try to do -
realistic-looking values, or filling in fields no Provider bothered to
declare. None of these three is a dependency of core Xfty, or of each
other. The capability comparison below is still worth reading in full - it's
what motivates why each pairing is worth having - but don't read a ❌ next
to XFTY's name as "and there's no way around it": see
Could XFTY pair with one of these to close a gap?
for which gaps are actually closed, and how. Only NBuilder and the
auto-mocking tools (AutoMoq/AutoNSubstitute) remain pure alternatives/
unrelated concerns, not pairings - see that same section for why.
Compared here: AutoFixture, Bogus, AutoBogus, and NBuilder. Maintenance-status and popularity claims below reflect this port's author's knowledge as of early 2026 - check each project's own repository for current activity before relying on that part.
| Primary job | |
|---|---|
| XFTY | Generate a graph of related records for a domain model that has real relationships - required/optional, shared, deferred - with a per-call choice of whether that graph gets mocked, left alone, or actually inserted through a pluggable persistence seam. |
| AutoFixture | Eliminate "Arrange" boilerplate in a unit test by auto-populating every property/constructor argument with an anonymous, deliberately-meaningless value ("it doesn't matter what this is, only that it's present"). Auto-mocking integration (AutoMoq, AutoNSubstitute) for dependencies. |
| Bogus | Generate realistic-looking fake data — names, addresses, emails, lorem ipsum, commerce/finance data, many locales — via an explicit per-property rule (Faker<T>().RuleFor(...)). |
| AutoBogus | AutoFixture's auto-population philosophy plus Bogus's realistic generators, picked by convention (a property named Email gets a fake email). |
| NBuilder | A fluent way to stamp out N similar objects with per-item overrides and simple sequential values. The simplest tool here. |
GitHub can't render a wide table without forcing a horizontal scrollbar, so this one stays to symbols only — the two prose sections right below it ("real edge" / "genuinely loses") carry the actual explanation for every row that needs one. Rows are grouped by whether XFTY has the capability at all (gaps at the bottom), then sorted most-common to least-common within each group, by how many of the five tools have it.
✅ yes · ❌ no · ◐ partial · — not applicable
| Capability | XFTY | AutoFixture | Bogus | AutoBogus | NBuilder |
|---|---|---|---|---|---|
| Fluent per-record override of specific fields | ✅ | ✅ | ✅ | ✅ | ✅ |
| Deep extensibility hook (custom strategy per type) | ✅ | ✅ | ✅ | ✅ | ◐ |
| Recursive nested-object population | ✅ | ✅ | ◐ | ✅ | ❌ |
| Self-referential / circular relationship cycle guard | ✅ | ◐ | — | ◐ | — |
| Runtime-conditioned recipe choice for the same type | ✅ | ◐ | ◐ | ❌ | ❌ |
| Sibling/ancestor/descendant-derived value, with a mis-ordering guard | ✅ | ❌ | ◐ | ❌ | ❌ |
| Required vs. optional relationship control, per call | ✅ | ❌ | ❌ | ❌ | ❌ |
| Shared parent deduplicated across many children | ✅ | ❌ | ❌ | ❌ | ❌ |
| Insert-mode abstraction (mock vs. actually persist) | ✅ | ❌ | ❌ | ❌ | ❌ |
| Dependency-ordered batch insert across mixed types | ✅ | ❌ | ❌ | ❌ | ❌ |
Graft onto an init-only model a real constructor rejects |
✅ | ❌ | ❌ | ❌ | ❌ |
| Realistic fake data | ◐ | ❌ | ✅ | ✅ | ❌ |
| Auto-populates every property, no rules written | ◐ | ✅ | ❌ | ✅ | ❌ |
| Auto-mocking of service dependencies (not data) | ❌ | ✅ | ❌ | ❌ | ❌ |
| Maturity / ecosystem (not a capability — kept last, unsorted) | new — first beta | very mature | mature | smaller, less active recently | older, largely superseded |
- It models a graph, not an object. Every tool above generates one thing
(recursively, in AutoFixture's/AutoBogus's case) per
Create/RuleForcall. XFTY generates a primary record plus its required and optional relationships, with per-call control over how deep that goes, and dedupes a shared parent across many children instead of creating one per child. None of the alternatives have an equivalent toSharedAncestoror toInsertInclusivity. - It has an opinion about persistence, without committing to one
technology.
InsertMode+IPersistenceGatewaymean the same Provider definitions serve a pure-in-memory unit test (Mock) and a real database integration test (Now, through EF Core, Dapper, or anything else) with no rewrite. The other four tools stop at "here's your populated object"; what happens to it next is entirely your own code.DepthBatchedInserterextends this to inserting a mixed-type graph in dependency order, one batch per depth level, instead of one call per object. - It can graft onto a model none of the alternatives can populate. An
init-only or constructor-validated model that rejects a builder pattern outright is still reachable viaInject/RecordInjector, which sets the relationship through reflection after the fact - useful for code under test that reads.Account.Nameoff a type your test can't otherwise wire. - Context-aware values are a first-class, guarded concept. A field
derived from a sibling, an ancestor several hops up, or (once the whole
graph exists) a child, with a loud error on a mis-ordered read instead of
a silently wrong
null. Bogus gets partway there (a rule can see the object being built) but has no ancestor/descendant graph to read from and no ordering guard. - Variant resolution is relationship-aware.
FlavouredLookupKey/DiscriminatorLookupKeylet a relationship pick a different Provider variant for its target based on a key or a predicate - not just "which customization is globally active right now."
Two of the three gaps below are gaps in core Xfty specifically, closed
by pairing with the tool that actually loses to (see
Could XFTY pair with one of these to close a gap?) -
worth choosing to add the pairing package, not something XFTY does for you
unasked.
- No realistic fake data in the core package. Core
Xftyships structural value expressions (IncrementingStringExpression,UniqueEmailExpression,LiteralExpression, theCopyFrom*family) but nothing that produces a plausible human name, street address, or paragraph of body text -Xfty.Bogus(FakeFullNameExpression,FakeEmailAddressExpression,FakeStreetAddressExpression,FakeParagraphExpression) closes that gap as a separate, opt-in package instead, so the base library never depends on Bogus. - No auto-population, by default. Every field a Provider cares about has
to be declared somewhere (a Master Template default, an override
template, a
Put(...)) - that's XFTY's own model, deliberately, and it doesn't change. AutoFixture's/AutoBogus's "just fill in everything, I'll tell you what matters" is a genuinely different, and for many unit tests simpler, default - and it's now available as an addition, not just an alternative:Xfty.AutoFixture/Xfty.AutoBoguslet AutoFixture/AutoBogus fill in whatever a Provider left unset, and/or generate straight through a Provider fromCreate<T>()/Generate<T>(). A project that wants AutoFixture's/AutoBogus's default for everything except the graph-shaped parts XFTY is good at no longer has to choose one tool over the other. - No auto-mocking story. AutoFixture's AutoMoq/AutoNSubstitute integration solves a different-but-adjacent problem (faking dependencies, not data) that XFTY doesn't touch at all, and pairing doesn't change that - a test that needs both still uses each tool for its own concern independently (see the last bullet of Could XFTY pair with one of these to close a gap?).
- A bigger API to learn.
Fixture.Create<T>()ornew Faker<T>().RuleFor(...)is a one-line mental model. XFTY's Providers, Master Templates, lookup keys, insert modes, and relationship inclusivity are a real API surface - proportionate to the graph-shaped problem it solves, but genuinely more to learn for a test that just needs one populated object. - No track record. AutoFixture and Bogus have a decade-plus of production use between them. XFTY is a first beta release of a from-scratch C# port; treat it accordingly.
These aren't all either/or choices anymore - the last two rows are ways of combining a tool from the first three with XFTY, not alternatives to it.
- One object, don't care what's in it: AutoFixture alone.
- One object, needs to look real (a demo, a UI screenshot, believable seed data): Bogus or AutoBogus alone.
- A short, disposable list of similar objects with a couple of tweaks:
NBuilder, or just a LINQ
Select- none of these tools are required for something this simple. - A whole related graph — parents, optional relationships, shared
ancestors, a validation rule that needs the graph to actually be
constraint-valid — and you want the exact same test to run against a mock
and a real database: XFTY, optionally with
Xfty.Bogusfor the realistic-value gap (FakeFullNameExpression,FakeEmailAddressExpression, and friends compose fine as ordinaryIValueExpressions). - That same graph, but you don't want to hand-declare every field on
every Provider - only the ones your test actually cares about: XFTY
paired with
Xfty.AutoFixtureorXfty.AutoBogus. Either lets AutoFixture/ AutoBogus fill in what a Provider left unset, or lets you callCreate<T>()/Generate<T>()straight through to a Provider - see Could XFTY pair with one of these to close a gap?.
Sometimes, and it's worth being specific about which gap and how much work
each pairing actually takes - "compose" is doing very different amounts of
work in each case below. As of this writing, XFTY pairs with all three of
Bogus, AutoFixture, and AutoBogus, each as its own opt-in package
(Xfty.Bogus, Xfty.AutoFixture, Xfty.AutoBogus) - none a dependency of
core Xfty or of each other.
- Bogus, for realistic values — done, as a separate package. An
IValueExpressionis just an interface; nothing stops it from calling aFaker<T>and returning the result.Xfty.Bogusbundles the common cases (FakeFullNameExpression,FakeEmailAddressExpression,FakeStreetAddressExpression,FakeParagraphExpression) so most Providers never have to write that wrapper themselves; writing a custom one for anything Bogus offers that isn't bundled still works exactly the same way. - AutoFixture, for auto-population — done, as a separate package, both
directions. The gap was genuine: a Provider must declare every field it
cares about, where AutoFixture's model is "fill in everything, then tell
me what you overrode."
Xfty.AutoFixturecloses it two ways that compose:XftyCustomizationpointsfixture.Create<T>()at a registeredRecordProviderinstead of AutoFixture's own generation (so XFTY's relationship/ancestor/cycle logic is what actually runs, not AutoFixture's own recursive auto-property population);IUnsetFieldFiller/AutoFixtureUnsetFieldFilleris the other direction - a fallback hookRecordProviderconsults for any field neither a Master Template, an override template,Put(...), nor a relationship configured, backed by an injectedIFixture. Not just "callFixture.Create<T>()yourself before handing the object to XFTY" (which already worked, but meant XFTY's own logic never saw or touched those fields) - the filler runs inside XFTY's own generation pipeline, late enough to never fight a Provider for a field it actually set. See use/autofixture.md and roadmap/autofixture-fallback-fill.md (design history). - AutoBogus, for auto-population plus realistic values together — done,
as a separate package, the same two directions as the AutoFixture
pairing.
Xfty.AutoBogusmirrorsXfty.AutoFixtureexactly:XftyAutoBogus.CreateFaker(lookup)pointsfaker.Generate<T>()at a registeredRecordProvider;AutoBogusUnsetFieldFilleris the sameIUnsetFieldFillerfallback hook, backed by anIAutoFakerinstead of anIFixture. One real difference worth knowing: AutoBogus never throws for a field that circles back on its own type (it self-limits recursion depth instead), where AutoFixture's default behavior does - see use/autobogus.md for the detail. Built onAutoGeneratorOverride's ownPreinitializeflag (confirmed, not assumed: set false, AutoBogus never constructs or populates its own instance before the override runs, so nothing it generates is thrown away or overwritten by XFTY's result). - NBuilder — not really a gap to close. XFTY already generates N
similar records natively (call a Provider's
Supply()in a loop, or useWith/WithChildrenfor the nested case); NBuilder's own niche is already inside what XFTY does, just with more ceremony for the trivial case. Pairing them adds a dependency without closing anything. - Auto-mocking (AutoMoq/AutoNSubstitute) — a different problem, not a gap XFTY has. These fake service dependencies your code under test calls; XFTY generates data records. A test that needs both just uses each tool for its own concern independently - there's no shared surface for them to compose across, so there's nothing to build here.
See also: reference/known-issues.md, roadmap/README.md.