Skip to content

Commit

Permalink
Add documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Corniel committed Aug 26, 2023
1 parent 45075cc commit ade53f1
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 10 deletions.
5 changes: 5 additions & 0 deletions Src/FluentAssertions/FluentAssertions.csproj
Expand Up @@ -70,6 +70,7 @@
<When Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
<ItemGroup>
<Compile Remove="Common/NullConfigurationStore.cs" />
<Compile Remove="Json/*.cs" />
<Compile Remove="SystemExtensions.cs" />
</ItemGroup>
<ItemGroup>
Expand All @@ -79,6 +80,7 @@
<When Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
<ItemGroup>
<Compile Remove="Common/NullConfigurationStore.cs" />
<Compile Remove="Json/*.cs" />
<Compile Remove="SystemExtensions.cs" />
</ItemGroup>
<ItemGroup>
Expand All @@ -89,6 +91,7 @@
<ItemGroup>
<Compile Remove="Common/AppSettingsConfigurationStore.cs" />
<Compile Remove="Common/ConfigurationStoreExceptionInterceptor.cs" />
<Compile Remove="Json/*.cs" />
<Compile Remove="SystemExtensions.cs" />
</ItemGroup>
</When>
Expand All @@ -98,6 +101,7 @@
<Compile Remove="Common/ConfigurationStoreExceptionInterceptor.cs" />
<Compile Remove="Events/*.cs" />
<Compile Remove="EventRaisingExtensions.cs" />
<Compile Remove="Json/*.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.0" />
Expand All @@ -106,6 +110,7 @@
<When Condition="'$(TargetFramework)' == 'net47'">
<ItemGroup>
<Compile Remove="Common/NullConfigurationStore.cs" />
<Compile Remove="Json/*.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.0" />
Expand Down
14 changes: 6 additions & 8 deletions Src/FluentAssertions/Json/JsonElementAssertions.cs
Expand Up @@ -86,7 +86,7 @@ public AndConstraint<JsonElementAssertions> BeString(string value, string becaus
/// <summary>
/// Asserts that the current <see cref="JsonElement"/> is the JSON number node.
/// </summary>
/// <param name="value">The value of the JSON string node.</param>
/// <param name="value">The value of the JSON number node.</param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
Expand All @@ -104,6 +104,10 @@ public AndConstraint<JsonElementAssertions> BeNumber(decimal value, string becau
return new(this);

Check notice on line 104 in Src/FluentAssertions/Json/JsonElementAssertions.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use preferred style of 'new' expression when created type is not evident

Missing type specification
}

/// <inheritdoc cref="BeNumber(decimal, string, object[])"/>
public AndConstraint<JsonElementAssertions> BeNumber(long value, string because = "", params object[] becauseArgs)
=> BeNumber((decimal)value, because, becauseArgs);

/// <summary>
/// Asserts that the current <see cref="JsonElement"/> is the JSON true node.
/// </summary>
Expand Down Expand Up @@ -144,11 +148,5 @@ public AndConstraint<JsonElementAssertions> BeFalse(string because = "", params
return new(this);

Check notice on line 148 in Src/FluentAssertions/Json/JsonElementAssertions.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use preferred style of 'new' expression when created type is not evident

Missing type specification
}

public void Be(long jsonNumber)
{
Execute.Assertion
.ForCondition(Subject.ValueKind == JsonValueKind.Number
&& Subject.GetInt64() == jsonNumber)
.FailWith("Expected {context:JSON} to be a number with value {0}, but got {1} instead.", jsonNumber, Subject.GetInt64());
}

}
13 changes: 11 additions & 2 deletions Src/FluentAssertions/Json/JsonSerializerOptionsAssertions.cs
@@ -1,5 +1,7 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.Json;
using FluentAssertions.Common;
using FluentAssertions.Execution;
Expand Down Expand Up @@ -37,7 +39,7 @@ public JsonSerializerOptionsAssertions(JsonSerializerOptions subject)
/// Zero or more objects to format using the placeholders in <see paramref="because" />.
/// </param>
/// <exception cref="ArgumentNullException"><paramref name="json"/> is <see langword="null"/>.</exception>
public AndConstraint<ValueWrapper<T>> Deserialize<T>(string json, string because = "", params object[] becauseArgs)
public AndConstraint<ValueWrapper<T>> Deserialize<T>(Stream json, string because = "", params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(json);

Expand All @@ -55,6 +57,13 @@ public AndConstraint<ValueWrapper<T>> Deserialize<T>(string json, string because
return new(new(deserialzed));

Check notice on line 57 in Src/FluentAssertions/Json/JsonSerializerOptionsAssertions.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use preferred style of 'new' expression when created type is not evident

Missing type specification

Check notice on line 57 in Src/FluentAssertions/Json/JsonSerializerOptionsAssertions.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use preferred style of 'new' expression when created type is not evident

Missing type specification
}

/// <inheritdoc cref="Deserialize{T}(Stream, string, object[])"/>
public AndConstraint<ValueWrapper<T>> Deserialize<T>(string json, string because = "", params object[] becauseArgs)
{
Stream stream = json is null ? null : new MemoryStream(Encoding.UTF8.GetBytes(json));
return Deserialize<T>(stream, because, becauseArgs);
}

/// <summary>
/// Asserts that the current <see cref="JsonSerializerOptions"/> can be used to serialize the specified value.
/// </summary>
Expand Down Expand Up @@ -83,7 +92,7 @@ public AndConstraint<ValueWrapper<JsonElement>> Serialize<T>(T value, string bec
return new(new(serialized));

Check notice on line 92 in Src/FluentAssertions/Json/JsonSerializerOptionsAssertions.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use preferred style of 'new' expression when created type is not evident

Missing type specification

Check notice on line 92 in Src/FluentAssertions/Json/JsonSerializerOptionsAssertions.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use preferred style of 'new' expression when created type is not evident

Missing type specification
}

private T TryDeserialize<T>(string json, out Exception failure)
private T TryDeserialize<T>(Stream json, out Exception failure)
{
try
{
Expand Down
103 changes: 103 additions & 0 deletions docs/_pages/json.md
@@ -0,0 +1,103 @@
---
title: Streams
permalink: /json/
layout: single
classes: wide
sidebar:
nav: "sidebar"
---

## JSON ##
A lot (if not the vast majority) of applications use JSON (de)serialization.
By doing so, how the serialization is performed is part of the public contract/API.
To prevent unexpected changes, writing tests that ensure the behavior is important.

### JSON serialization options
To ensure that the assertions are done on the serialization options that are registered, it might be worth explicitly resolve those options:

```csharp
public class Serialization : Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<Program>
{
private JsonSerializerOptions SerializerOptions => Services.GetRequiredService<JsonSerializerOptions>();
}
```

Both a `Serialize()` and a `Deserialize()` can be performed:

```csharp
JsonSerializerOptions options = GetOptions();

options.Should().Serialize(someObject);
options.Should().Deserialize(someJson);
```

And because we specially interested in the results of these actions, we want to assert the outcomes to:

```csharp
public class Serializes
{
[Fact]
public void DateOnly_as_string_value()
{
JsonSerializerOptions options = GetOptions();

options.Should().Serialize(new DateOnly(2017, 06, 11))
.And.Value.Should().BeString("2017-06-11");
}

[Fact]
public void Enum_as_number()
{

JsonSerializerOptions options = GetOptions();

options.Should().Serialize(SomeEnum.SomeValue)
.And.Value.Should().BeNumber(42);
}

[Fact]
pulic void Default_of_custom_struct_as_null()
{
JsonSerializerOptions options = GetOptions();

options.Should().Serialize(default(CustomStruct))
.And.Value.Should().BeNull();
}
}
```

And

```csharp
public class Deserializes
{
[Fact]
public void DateOnly_from_JSON_string()
{
JsonSerializerOptions options = GetOptions();

options.Should().Deserialize<DateOnly>("2017-06-11")
.And.Value.Should().Be(new DateOnly(2017-06-11));
}

[Fact]
public void Enum_from_number()
{

JsonSerializerOptions options = GetOptions();

options.Should().Deserialize<SomeEnum>("42")
.And.Value.Should().BeNumber(SomeEnum.SomeValue);
}

[Fact]
public void Enum_from_string()
{

JsonSerializerOptions options = GetOptions();

options.Should().Deserialize<SomeEnum>("\"SomeValue\"")
.And.Value.Should().BeNumber(SomeEnum.SomeValue);
}
}
```

0 comments on commit ade53f1

Please sign in to comment.