Persistord.Testing 1.0.0-beta.3

This is a prerelease version of Persistord.Testing.
dotnet add package Persistord.Testing --version 1.0.0-beta.3
                    
NuGet\Install-Package Persistord.Testing -Version 1.0.0-beta.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Persistord.Testing" Version="1.0.0-beta.3">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Persistord.Testing" Version="1.0.0-beta.3" />
                    
Directory.Packages.props
<PackageReference Include="Persistord.Testing">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Persistord.Testing --version 1.0.0-beta.3
                    
#r "nuget: Persistord.Testing, 1.0.0-beta.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Persistord.Testing@1.0.0-beta.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Persistord.Testing&version=1.0.0-beta.3&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Persistord.Testing&version=1.0.0-beta.3&prerelease
                    
Install as a Cake Tool

Persistord.Testing

<div align="center">

NuGet Downloads

← Persistord docs · Documentation site

</div>

Test-only helpers for Persistord-based DbContexts: an in-memory SQLite fixture and the IModelCacheKeyFactory it needs to give every test a fresh model. It is safe — and intended — to reference from test projects only; it ships no runtime dependency your bot needs in production.

SqliteTestDatabase.Private — the default

Private opens one SQLite connection to DataSource=:memory: and hands that same connection object to every context you create from it, because :memory: is a different database per connection — without sharing the connection, a second context would see an empty database.

await using var database = SqliteTestDatabase.Private();
await using var context = database.CreateContext<MyContext>(o => new MyContext(o));

context.Widgets.Add(new Widget { GuildId = 1UL, Key = "a" });
await context.SaveChangesAsync();

SqliteTestDatabase.Shared — independent connections, same data

Private breaks down the moment your code under test opens its own connection — a DI scope, a background writer, two contexts racing on the same row. Shared uses SQLite's shared-cache mode instead: it hands out a connection string, so every caller opens its own connection, and all of them see the same in-memory database.

await using var database = SqliteTestDatabase.Shared();

await using (var writer = database.CreateContext<MyContext>(o => new MyContext(o)))
{
    writer.Widgets.Add(new Widget { GuildId = 1UL, Key = "a" });
    await writer.SaveChangesAsync();
}

// A second, independent connection — built from the connection string, not the
// connection object — still sees the row the first one wrote. This only works
// because `writer`, above, came from `CreateContext` and already built the schema:
// `Options` itself never calls Migrate/EnsureCreated, so a `Shared` database needs
// at least one `CreateContext` call before any `Options`-only reader can query it.
await using var reader = new MyContext(database.Options<MyContext>());

TestSchema.Migrate vs TestSchema.EnsureCreated

Migrate (the default) applies your context's committed migrations, so a model that has drifted from what is actually checked in fails in the test instead of in production. EnsureCreated builds the schema straight from the current model instead — faster, but blind to migration drift. Pass TestSchema.EnsureCreated to Private/Shared when a test does not care about migrations and speed matters more.

Overriding the base setup: configure

Options and CreateContext each have an overload that takes an Action<DbContextOptionsBuilder<TContext>> configure, applied after the base setup (the connection, the model cache key factory, the interceptors) so it can override anything — most importantly, to pin a context-wide QueryTrackingBehavior. This matters for regression tests that must reproduce a bug that only showed up under QueryTrackingBehavior.NoTracking:

await using var context = database.CreateContext<MyContext>(
    o => new MyContext(o),
    configure: builder => builder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));

There are two overloads rather than one optional configure parameter, because an optional parameter cannot precede a params array without call-site ambiguity.

Model assertions

ModelAssertions are three DbContext extension methods about the shape of the model, so a schema test is one line instead of a seed-mutate-assert round trip against the database. Every failure throws a plain InvalidOperationException naming the entity and what was expected — the package takes no dependency on any test framework, so these work from xunit, NUnit and MSTest alike.

// A unique index over exactly these properties, in order.
context.AssertUniqueIndex<Membership>(nameof(Membership.GuildId), nameof(Membership.UserId));

// The child cascades from the parent: the one-line replacement for an
// insert-parent, insert-child, delete-parent, assert-empty test.
context.AssertCascade<Membership, GuildEntity>();

// The entity's primary key is a caller-supplied ulong, never store-generated, and
// stored as a long — the shape ApplyGuildRoot and DiscordDbContext expect.
context.AssertSnowflakeKey<GuildEntity>();

AssertCascade throws if the child has more than one foreign key to the parent — it does not guess which one you mean by picking whichever one EF happens to return first. A type with two relationships to the same principal needs a more specific, hand-written assertion instead.

UniqueModelCacheKeyFactory

EF Core caches the compiled model per context type by default, so the model is normally built once for the whole test run. SqliteTestDatabase replaces that cache key factory with UniqueModelCacheKeyFactory, which returns a new key on every call, so every context you create gets a freshly built model. Without it:

  • Per-test coverage of your entity configurations collapses to whichever test happens to run first and builds the model.
  • Configuration mutants (a Stryker mutation that changes HasIndex or IsRequired, say) can survive, because the mutated configuration never actually ran for the tests that would have caught it.
  • Two contexts configured differently in the same run could silently share one cached model.

You do not need to reference UniqueModelCacheKeyFactory yourself — SqliteTestDatabase installs it on every context it builds.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-beta.3 78 9/10/2026