PactEf.Verify 0.1.0-beta.1

This is a prerelease version of PactEf.Verify.
dotnet add package PactEf.Verify --version 0.1.0-beta.1
                    
NuGet\Install-Package PactEf.Verify -Version 0.1.0-beta.1
                    
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="PactEf.Verify" Version="0.1.0-beta.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PactEf.Verify" Version="0.1.0-beta.1" />
                    
Directory.Packages.props
<PackageReference Include="PactEf.Verify" />
                    
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 PactEf.Verify --version 0.1.0-beta.1
                    
#r "nuget: PactEf.Verify, 0.1.0-beta.1"
                    
#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 PactEf.Verify@0.1.0-beta.1
                    
#: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=PactEf.Verify&version=0.1.0-beta.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=PactEf.Verify&version=0.1.0-beta.1&prerelease
                    
Install as a Cake Tool

PactEf

Consumer-driven contract testing for EF Core database schemas.

PactEf intercepts the SQL that consumer integration tests actually execute, writes those queries as a JSON snapshot ("pact"), and replays them via EXPLAIN against a fresh database at migration time. If a migration breaks a query — renamed column, dropped table, type change — the verification test fails before the change ships.

How It Works

Consumer tests run          Producer runs migrations
      |                              |
      v                              v
EF Core interceptor          Testcontainers spins up
captures SQL queries         a fresh Postgres instance
      |                              |
      v                              v
  SampleConsumer.json    -----> PactEfVerifier replays
  (snapshot / pact)             each query via EXPLAIN
                                     |
                              Pass / Fail report

Packages

Package Purpose
PactEf.Core Shared models: QueryEntry, SnapshotFile, SnapshotSerializer
PactEf.Capture EF Core interceptor that records SQL during consumer tests
PactEf.Verify Loads snapshots and verifies them against the current schema

Quick Start

1. Consumer side — capture queries

Add PactEf.Capture to your consumer test project and wire up the interceptor:

// In your test fixture
var options = new DbContextOptionsBuilder<MyDbContext>()
    .UseNpgsql(connectionString)
    .AddPactEfCapture(o => o.ConsumerName = "MyConsumer")
    .Options;

Register the assembly fixture so snapshots are flushed after the test run:

// AssemblyInfo.cs
[assembly: TestFramework("Xunit.Extensions.AssemblyFixture.XunitTestFramework",
                          "Xunit.Extensions.AssemblyFixture")]

// Test class
public class MyTests : IAssemblyFixture<PactEfAssemblyFixture>

Set the environment variable so capture activates:

export DOTNET_ENVIRONMENT=Testing
# or ASPNETCORE_ENVIRONMENT=Testing

Snapshots are written to pactef-snapshots/<ConsumerName>.json next to the test project.

2. Producer side — verify against current schema

Add PactEf.Verify to your database project and add a verification test:

[Fact]
[Trait("Category", "PactEfVerification")]
public async Task AllConsumerSnapshots_AreCompatibleWithCurrentSchema()
{
    await PactEfVerifier.VerifyAllAsync(options =>
    {
        options.SnapshotSources =
        [
            SnapshotSource.FromFolder("/path/to/consumers/my-consumer/pactef-snapshots"),
            SnapshotSource.FromEnvVariable("PACTEF_SNAPSHOT_PATHS"), // local override
        ];
        options.ConnectionString = _container.GetConnectionString();
        options.Provider = DbProvider.PostgreSql;
        options.DefaultMode = VerificationMode.Explain;
    });
}

The test spins up a real database (Testcontainers), applies all migrations, then runs EXPLAIN for each captured query. Any column/table/type mismatch fails the test with a detailed report.

Environment Variables

Variable Default Purpose
ASPNETCORE_ENVIRONMENT Must equal Testing for capture to activate
PACTEF_CAPTURE_DISABLED Set to any value to disable capture regardless of environment
PACTEF_SNAPSHOT_PATHS Semicolon-separated absolute paths; overrides FromFolder sources for local dev

Snapshot Format

Current format is v2 ("schemaVersion": "2.0").

{
  "schemaVersion": "2.0",
  "consumerName": "SampleConsumer",
  "capturedAt": "2026-05-18T16:42:30Z",
  "dbSchemaVersion": "20260514000000_InitialCreate",
  "queries": [
    {
      "sql": "INSERT INTO \"OrderItems\" (\"Description\", \"OrderId\")\nVALUES (@p0, @p1)\nRETURNING \"Id\";\n",
      "parameterTypes": ["String", "Int32"],
      "parameters": [
        {
          "name": "@p0",
          "clrType": "String",
          "dbType": "String",
          "storeType": "Varchar",
          "maxLength": 1000,
          "isNullable": true
        },
        {
          "name": "@p1",
          "clrType": "Int32",
          "dbType": "Int32",
          "storeType": "Integer",
          "isNullable": false
        }
      ],
      "executionCount": 1
    }
  ]
}

parameters[] (v2 metadata) map 1:1 to ParameterMetadata: name, clrType, dbType, storeType, maxLength, precision, scale, isNullable, size. All fields nullable — null means unknown, not unconstrained; a missing maxLength/isNullable just means capture couldn't resolve the column, not that it's unbounded/NOT NULL.

name matters: parameters order follows DbCommand.Parameters order, which is not SQL appearance order (e.g. EF Core emits UPDATE ... SET "C" = @p0 WHERE "Id" = @p1 with @p1 bound first). Verification matches parameters to placeholders by name, falling back to position only for legacy v1 snapshots or unnamed/ambiguous params — matching positionally in the general case can substitute a boundary literal into the wrong column.

parameterTypes is still written for backward compatibility but is legacy; parameters is authoritative. Old v1 snapshots (no parameters) still load and verify — SnapshotSerializer backfills one ParameterMetadata per entry from parameterTypes (only ClrType set, no boundary variants from the consumer side).

Verification Modes

Mode Behaviour
Explain Runs EXPLAIN <sql> — catches schema errors without touching data. Default.
Execute Runs the query with substituted literal values — catches runtime errors too.

Repository Layout

src/
  PactEf.Core/              Models and serialization
  PactEf.Core.Tests/
  PactEf.Capture/           EF Core interceptor, xUnit fixtures
  PactEf.Capture.Tests/
  PactEf.Verify/            Snapshot loader, verifier, failure report
  PactEf.Verify.Tests/
samples/
  SampleDb/                 Entity model, migrations, SchemaVerificationTests
  SampleConsumer/           OrderRepository using SampleDb
  SampleConsumer.Tests/     Integration tests that capture SQL

Running Tests

# Unit tests (fast, no Docker required)
dotnet test src/PactEf.Core.Tests/PactEf.Core.Tests.csproj
dotnet test src/PactEf.Capture.Tests/PactEf.Capture.Tests.csproj
dotnet test src/PactEf.Verify.Tests/PactEf.Verify.Tests.csproj

# Consumer integration tests (requires Docker — captures snapshot)
dotnet test samples/SampleConsumer.Tests/SampleConsumer.Tests.csproj

# Schema verification (requires Docker — verifies snapshot against schema)
export PACTEF_SNAPSHOT_PATHS=/absolute/path/to/samples/SampleConsumer.Tests/pactef-snapshots
dotnet test samples/SampleDb/SampleDb.csproj --filter "Category=PactEfVerification"

Stack

  • .NET 10, C# 13
  • EF Core 9 (interceptors)
  • Npgsql / PostgreSQL
  • xUnit v2 + Xunit.Extensions.AssemblyFixture
  • Testcontainers.PostgreSql (samples only)
  • System.Text.Json
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
0.1.0-beta.1 63 8/18/2026