PayloadGremlin 1.0.3

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

PayloadGremlin

PayloadGremlin

NuGet .NET Build License: MIT Tests

Target framework: net10.0 · Language: C# 14 · Test runner: xUnit

PayloadGremlin makes good JSON payloads misbehave on purpose.

PayloadGremlin is a small .NET library for generating realistic bad JSON variants from known-good payloads. Use it in unit and integration tests to find brittle deserializers, API clients, and downstream code before production does.

This is not a general random fuzzer. It focuses on the kind of contract drift that shows up in real API integrations: nulls where you expected strings, numbers sent as strings, date format changes, unexpected properties, and casing mismatches.

Why it exists

Production APIs change. Legacy systems send odd formats. Deserializers fail in subtle ways. Random fuzzing can miss the failures that actually happen in the wild.

PayloadGremlin gives you:

  • Named, realistic mutation profiles
  • Deterministic output when you set a seed
  • Per-path configuration
  • Mutation metadata so failing tests are easy to understand
  • Simple failure shrinking for multi-mutation cases

Installation

dotnet add package PayloadGremlin

See PUBLISHING.md for how releases are published via NuGet trusted publishing.

Quick start

using PayloadGremlin;

var json = """
{
  "customerId": "123",
  "premium": 1200.50,
  "active": true,
  "startDate": "2026-01-01"
}
""";

var gremlin = global::PayloadGremlin.PayloadGremlin.Create(options =>
{
    options.WithSeed(12345);
    options.UseProfile(GremlinProfile.RealisticApiDrift);
    options.MaxCases(10);
});

var result = gremlin.Generate(json);

foreach (var testCase in result.Cases)
{
    Console.WriteLine(testCase.Name);
    Console.WriteLine(testCase.Payload);
}

Profiles

Profiles enable sensible sets of mutations without listing each type manually.

Profile What it exercises
RealisticApiDrift Common drift: nulls, type coercion, date formats, casing
StrictClientBreaker Missing fields, type swaps, empty collections
LegacySystemWeirdness Whitespace, unicode, comma decimals, string booleans
DateAndMoneyChaos Dates, decimals, numeric edge cases
Aggressive Widest mutation set (still valid JSON by default)
options.UseProfile(GremlinProfile.StrictClientBreaker);

You can also enable specific mutation types:

options.Enable(MutationType.RemoveProperty);
options.Enable(MutationType.NumberToString);
options.Enable(MutationType.DateFormatChange);

Path configuration

Target or protect specific JSON paths:

var gremlin = global::PayloadGremlin.PayloadGremlin.Create(options =>
{
    options.WithSeed(12345);
    options.UseProfile(GremlinProfile.RealisticApiDrift);

    options.ForPath("$.customerId", path =>
    {
        path.DoNotRemove();
        path.AllowNull(false);
    });

    options.ExcludePath("$.metadata.traceId");
});

Deterministic seeds

The same input JSON, configuration, and seed always produce the same cases in the same order.

options.WithSeed(42);

When a test fails, use the seed and mutation metadata to reproduce the exact case.

Mutation metadata

Every case includes AppliedMutation records with:

  • Mutation id
  • JSON path
  • Mutation type
  • Before / after values
  • Severity
  • Whether the output is valid JSON
  • Human-readable description

The reproduction seed is on each GremlinCase (testCase.Seed), not on individual mutations.

foreach (var mutation in testCase.Mutations)
{
    Console.WriteLine($"{mutation.MutationType} @ {mutation.JsonPath}");
    Console.WriteLine($"  {mutation.Description}");
}

Shrinking

If a test fails on a case with multiple mutations, ask for smaller versions:

var smallerCases = gremlin.Shrink(failingCase);

v1 removes one mutation at a time to help isolate the smallest failing payload.

Reports

var result = gremlin.Generate(json);
Console.WriteLine(result.Report.ToMarkdown());

The report summarises total cases, mutation types used, paths touched, paths never touched, invalid JSON count, and the reproduction seed.

Invalid JSON mode

By default, generated payloads stay valid JSON. To include syntactic breakage:

options.AllowInvalidJson();

This enables truncated JSON, trailing commas, and broken string quotes.

Example xUnit usage

[Theory]
[MemberData(nameof(GremlinCases))]
public void Deserialize_HandlesMutatedPayloads(string name, string payload)
{
    var result = MyDeserializer.TryParse(payload, out var model);
    Assert.True(result, $"Failed on case: {name}");
}

public static IEnumerable<object[]> GremlinCases()
{
    var json = File.ReadAllText("Fixtures/order.json");
    var result = global::PayloadGremlin.PayloadGremlin.Create(o =>
    {
        o.WithSeed(42);
        o.UseProfile(GremlinProfile.RealisticApiDrift);
        o.MaxCases(25);
    }).Generate(json);

    return result.Cases.Select(c => new object[] { c.Name, c.Payload });
}

Building from source

dotnet build
dotnet test
dotnet run --project samples/PayloadGremlin.Sample

Contributing

Contributions are welcome. See CONTRIBUTING.md for setup, project layout, and how to add mutations or profiles.

Please read our Code of Conduct before participating.

Quick start for contributors:

git clone https://github.com/kearns2000/payloadgremlin.git
cd PayloadGremlin
dotnet build -c Release
dotnet test -c Release

Open a pull request with tests for any behaviour change. CI runs build and test on every PR.

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.
  • net10.0

    • No dependencies.

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.3 55 7/2/2026
1.0.2 45 7/2/2026
1.0.1 46 7/2/2026
1.0.0 41 7/2/2026