Guides
No-DB unit tests
Use Biome without a store, for pure unit tests.
Omit the store
If the behavior is a pure function over entities, a database adds setup without helping the assertion. You can use the same blueprints without a store. Biome defaults to NullStore, and entities exist as soon as each Add returns, so there is no need to call BuildAsync unless you want save hooks to run.
TestBiome
.Start(out _) // NullStore; no BuildAsync needed
.AddCodeRepository(out var repo)
.AddPullRequests(out var prs, PullRequests.Draft, PullRequests.Merged);
// act: var summary = PullRequestSummary.From(repo, prs);
var now = DateTimeOffset.UtcNow;
var repo = new CodeRepository
{
Id = $"repo_{Guid.CreateVersion7():N}",
OrganizationId = "org_test",
OwnerQualifiedName = "zeeq-ai/example",
Enabled = true,
CreatedAtUtc = now,
};
var prs = new[]
{
new PullRequestRecord
{
Id = $"pr_{Guid.CreateVersion7():N}",
RepositoryId = repo.Id,
PullRequestNumber = 1,
Title = "Pull request 1",
Branch = "feature/1",
IsDraft = true,
State = PullRequestState.Open,
},
new PullRequestRecord
{
Id = $"pr_{Guid.CreateVersion7():N}",
RepositoryId = repo.Id,
PullRequestNumber = 2,
Title = "Pull request 2",
Branch = "feature/2",
State = PullRequestState.Merged,
},
};
// act: var summary = PullRequestSummary.From(repo, prs);
Call
BuildAsync only when you need a save or the save hooks. For a pure function over entities, building is enough.When you still want a store
A RecordingStore is a handy test double: it captures the rows Biome would save, so you can assert on order and exclusions without a database. The library's own tests use it:
RecordingStore.cs
internal sealed class RecordingStore : IBiomeStore
{
internal List<object> Entities { get; } = [];
public ValueTask SaveAsync(
IReadOnlyList<object> entities,
CancellationToken cancellationToken
)
{
Entities.AddRange(entities);
return ValueTask.CompletedTask;
}
}
CodeReviewTests.cs
var store = new RecordingStore();
var biome = TestBiome.Start(out var seed, store);
await biome.AddLockedReview(out var activeLock).BuildAsync();
// seed rows precede the four created rows
// store.Entities[0] == seed.Owner; store.Entities[1] == seed.Organization;
// store.Entities.Count == 6
Inspecting what was built
Keep the starting builder when you want entities a helper created, without naming each:
Inspect
var biome = TestBiome.Start(out _);
await biome.AddLockedReview(out var activeLock).BuildAsync();
var review = biome.Last<CodeReviewRecord>(); // created by the helper
// creation order; includes transients; excludes seed rows
var repos = biome.All<CodeRepository>();
All<T>() and Last<T>() include transient entities and aggregate wrappers, but not seed rows. Prefer naming entities with out when the test is about them, and use All / Last for entities that helpers create.