aweXpect.Json 2.0.0

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

aweXpect.Json

Changelog

Nuget Coverage Mutation testing badge

Expectations for JSON strings and System.Text.Json for aweXpect.

Overview

Expectation Subject Summary
IsEqualTo(…).AsJson() string equal to the expected JSON, compared by structure
IsValidJson string can be parsed as JSON
IsValidJsonMatching string valid JSON that matches the expected value
IsValidJsonMatchingExactly string valid JSON that matches the expected value exactly
Matches JsonElement matches the expected value, additional properties allowed
MatchesExactly JsonElement matches the expected value exactly
IsObject JsonElement a JSON object, optionally with expectations on it
IsArray JsonElement a JSON array, optionally with expectations on it
IsJsonSerializable object survives a round trip through the JSON serializer

String comparison as JSON

You can verify that a string is equal to the expected JSON, compared by its structure instead of its formatting:

string track = """{"title":"Let It Be","artist":"The Beatles","durationSeconds":243}""";
string expected = """
                  {
                    "title": "Let It Be",
                    "artist": "The Beatles",
                    "durationSeconds": 243
                  }
                  """;

await Expect.That(track).IsEqualTo(expected).AsJson();

If the track had a duration of 241 seconds instead, the expectation would fail with:

Expected that track
is JSON equivalent to {
  "title": "Let It Be",
  "artist": "The Beatles",
  "durationSeconds": 243
},
but it differed as $.durationSeconds was 241 instead of 243

Actual:
{"title":"Let It Be","artist":"The Beatles","durationSeconds":241}

Expected:
{
  "title": "Let It Be",
  "artist": "The Beatles",
  "durationSeconds": 243
}

The expected string must be valid JSON, otherwise an ArgumentException is thrown. As JSON is compared by its structure, AsJson() cannot be combined with IgnoringCase() or Using(comparer).

AsJson() is not available on Contains or DoesNotContain for a string subject, as parts of a JSON string are usually not valid JSON. Use IsEqualTo(expected).AsJson() or IsValidJsonMatching(expected) instead. On a collection of strings, Contains(expected).AsJson() compares each item as JSON.

Validation

You can verify that a string is valid JSON:

string track = """{"title": "Let It Be"}""";

await Expect.That(track).IsValidJson();

If the closing brace was missing, the expectation would fail with:

Expected that track
is valid JSON,
but it could not be parsed: Expected depth to be zero at the end of the JSON payload. There is an open JSON object or array that should be closed. LineNumber: 0 | BytePositionInLine: 21.

This verifies that the string can be parsed by JsonDocument.Parse without exceptions. You can also specify the JsonDocumentOptions:

string track = """{"title": "Let It Be"}""";

await Expect.That(track).IsValidJson(o => o with { CommentHandling = JsonCommentHandling.Disallow });

You can add further expectations on the JsonElement parsed from the subject:

string track = """{"title": "Let It Be"}""";

await Expect.That(track).IsValidJson().Which(j => j.Matches(new { title = "Let It Be" }));

Or you can verify that the string is valid JSON and matches an expected value in one step:

string track = """{"title": "Let It Be", "artist": "The Beatles"}""";

await Expect.That(track).IsValidJsonMatching(new { title = "Let It Be" });
await Expect.That(track).IsValidJsonMatchingExactly(new { title = "Let It Be", artist = "The Beatles" });

IsValidJsonMatching(new { title = "Yesterday" }) would fail with:

Expected that track
is valid JSON which matches new { title = "Yesterday" },
but it differed as $.title was "Let It Be" instead of "Yesterday"

JsonElement

Matches

You can verify that a JsonElement matches an expected object. Matches ignores additional properties, while MatchesExactly also fails for them:

JsonElement track = JsonDocument.Parse("""{"title": "Let It Be", "artist": "The Beatles"}""").RootElement;

await Expect.That(track).Matches(new { title = "Let It Be" });
await Expect.That(track).MatchesExactly(new { title = "Let It Be", artist = "The Beatles" });

MatchesExactly(new { title = "Let It Be" }) would fail because of the additional artist property:

Expected that track
matches new { title = "Let It Be" } exactly,
but it differed as $.artist had unexpected "The Beatles"

You can verify that a JsonElement matches an expected array:

JsonElement titles = JsonDocument.Parse("""["Let It Be", "Yesterday", "Hey Jude"]""").RootElement;

await Expect.That(titles).Matches(["Let It Be", "Yesterday"]);
await Expect.That(titles).MatchesExactly(["Let It Be", "Yesterday", "Hey Jude"]);

You can also verify that a JsonElement matches a primitive value:

await Expect.That(JsonDocument.Parse("\"Let It Be\"").RootElement).Matches("Let It Be");
await Expect.That(JsonDocument.Parse("4.05").RootElement).Matches(4.05);
await Expect.That(JsonDocument.Parse("true").RootElement).Matches(true);
await Expect.That(JsonDocument.Parse("null").RootElement).Matches(null);

IsObject

You can verify that a JsonElement is a JSON object that satisfies some expectations:

JsonElement track = JsonDocument.Parse("""{"title": "Let It Be", "artist": "The Beatles"}""").RootElement;

await Expect.That(track).IsObject(o => o
    .With("title").Matching("Let It Be").And
    .With("artist").Matching("The Beatles"));

With .With("artist").Matching("The Rolling Stones") instead, the expectation would fail with:

Expected that track
is an object and $.title matches "Let It Be" and $.artist matches "The Rolling Stones",
but it differed as $.artist was "The Beatles" instead of "The Rolling Stones"

You can verify that a property is another object, recursively:

JsonElement track = JsonDocument.Parse(
    """{"title": "Let It Be", "album": {"title": "Let It Be", "year": 1970}}""").RootElement;

await Expect.That(track).IsObject(o => o
    .With("album").AnObject(album => album
        .With("year").Matching(1970)));

You can verify that a property is an array:

JsonElement album = JsonDocument.Parse(
    """{"title": "Abbey Road", "tracks": ["Come Together", "Something"]}""").RootElement;

await Expect.That(album).IsObject(o => o
    .With("tracks").AnArray(tracks => tracks.WithElements("Come Together", "Something")));

You can verify the number of properties in a JSON object:

JsonElement track = JsonDocument.Parse("""{"title": "Let It Be", "artist": "The Beatles"}""").RootElement;

await Expect.That(track).IsObject(o => o.With(2).Properties());

IsArray

You can verify that a JsonElement is a JSON array that satisfies some expectations:

JsonElement titles = JsonDocument.Parse("""["Let It Be", "Yesterday"]""").RootElement;

await Expect.That(titles).IsArray(a => a
    .At(0).Matching("Let It Be").And
    .At(1).Matching("Yesterday"));

You can verify the number of elements in a JSON array:

JsonElement titles = JsonDocument.Parse("""["Let It Be", "Yesterday"]""").RootElement;

await Expect.That(titles).IsArray(a => a.With(2).Elements());

You can match the expected elements of an array directly:

JsonElement titles = JsonDocument.Parse("""["Let It Be", "Yesterday"]""").RootElement;

await Expect.That(titles).IsArray(a => a.WithElements("Let It Be", "Yesterday"));

With WithElements("Let It Be", "Hey Jude") instead, the expectation would fail with:

Expected that titles
is an array and $[0] matches "Let It Be" and $[1] matches "Hey Jude",
but it differed as $[1] was "Yesterday" instead of "Hey Jude"

You can match nested arrays recursively (use null to skip an element):

JsonElement sides = JsonDocument.Parse(
    """[["Come Together", "Something"], ["Here Comes the Sun"], ["The End", "Her Majesty"]]""").RootElement;

await Expect.That(sides).IsArray(a => a
    .WithArrays(
        side => side.WithElements("Come Together", "Something"),
        null,
        side => side.At(1).Matching("Her Majesty")));

You can match objects in an array recursively (use null to skip an element):

JsonElement tracks = JsonDocument.Parse(
    """
    [
      {"title": "Let It Be"},
      {"title": "Yesterday"},
      {"title": "Hey Jude", "durationSeconds": 431}
    ]
    """).RootElement;

await Expect.That(tracks).IsArray(a => a
    .WithObjects(
        track => track.With("title").Matching("Let It Be"),
        null,
        track => track.With(2).Properties()));

JSON serializable

You can verify that an object is JSON serializable:

public record Track(string Title, string Artist);

Track track = new("Let It Be", "The Beatles");

await Expect.That(track).IsJsonSerializable();

This verifies that the object can be serialized to JSON and deserialized again, and that the result is equivalent to the original object. For example, a property with a private setter loses its value:

public class PlayCounter
{
    public string Title { get; set; } = "";
    public int PlayCount { get; private set; }
    public void Play() => PlayCount++;
}

PlayCounter counter = new() { Title = "Let It Be" };
counter.Play();

await Expect.That(counter).IsJsonSerializable();
Expected that counter
is serializable as JSON,
but it was not:
  Property PlayCount differed:
      Actual: 0
    Expected: 1

You can specify both the JsonSerializerOptions and the equivalency options:

await Expect.That(track).IsJsonSerializable(
    new JsonSerializerOptions { IncludeFields = true },
    e => e.IgnoringMember(nameof(Track.Artist)));

You can also specify the type that the subject is deserialized into:

object track = new Track("Let It Be", "The Beatles");

await Expect.That(track).IsJsonSerializable<Track>(
    new JsonSerializerOptions { IncludeFields = true },
    e => e.IgnoringMember(nameof(Track.Artist)));

Customization

You can change the default JsonDocumentOptions, used to parse JSON strings, and the default JsonSerializerOptions, used to format JSON values. Both allow trailing commas by default. Each value is set on its own and restored when the returned lifetime is disposed:

using (Customize.aweXpect.Json().DefaultJsonDocumentOptions.Set(new JsonDocumentOptions
       {
           AllowTrailingCommas = false,
       }))
{
    // parses JSON strings without allowing trailing commas
}

To change a value for all tests, set it on Customize.aweXpect.Global.Json() instead.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos 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
2.0.0 71 10/10/2026
2.0.0-pre.2 256 10/8/2026
2.0.0-pre.1 120 9/28/2026
1.8.0 370 9/5/2026
1.7.0 304 5/10/2026
1.6.0 694 10/18/2025
1.5.0 386 9/18/2025
1.4.0 371 9/10/2025
1.3.0 390 6/30/2025
1.2.0 258 5/10/2025
1.1.0 428 4/1/2025
1.0.0 288 3/19/2025
1.0.0-pre.3 221 3/19/2025
1.0.0-pre.2 203 3/16/2025
1.0.0-pre.1 265 3/14/2025
0.6.0 237 3/1/2025
0.5.0 374 2/28/2025
0.4.0 315 2/24/2025
0.3.0 546 2/17/2025
0.2.0 208 2/16/2025
Loading failed