diff --git a/.github/workflows/github-actions-release.yml b/.github/workflows/github-actions-release.yml index aaacc5d..cd293ce 100644 --- a/.github/workflows/github-actions-release.yml +++ b/.github/workflows/github-actions-release.yml @@ -7,7 +7,7 @@ on: type: string description: The version of the library required: true - default: 1.3.0 + default: 1.4.0 VersionSuffix: type: string description: The version suffix of the library (for example rc.1) diff --git a/PosInformatique.FluentAssertions.Json.sln b/PosInformatique.FluentAssertions.Json.sln index 6c91e91..204080f 100644 --- a/PosInformatique.FluentAssertions.Json.sln +++ b/PosInformatique.FluentAssertions.Json.sln @@ -22,6 +22,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7928175B tests\.editorconfig = tests\.editorconfig EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{F618D0FB-ECB8-4E1B-BAD3-5B3618FB8EB2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{FFCF0F26-C911-4595-BEF0-9393206E5FDD}" + ProjectSection(SolutionItems) = preProject + .github\workflows\github-actions-ci.yaml = .github\workflows\github-actions-ci.yaml + .github\workflows\github-actions-release.yml = .github\workflows\github-actions-release.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -42,6 +50,8 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {7928175B-C3B3-4F81-B956-BF4E4F816436} = {882949E5-7DCE-4EB6-8E9A-CB88FD0ED1F9} + {F618D0FB-ECB8-4E1B-BAD3-5B3618FB8EB2} = {882949E5-7DCE-4EB6-8E9A-CB88FD0ED1F9} + {FFCF0F26-C911-4595-BEF0-9393206E5FDD} = {F618D0FB-ECB8-4E1B-BAD3-5B3618FB8EB2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0582B8EB-4FA4-488E-9953-9B7CEEE4E94F} diff --git a/README.md b/README.md index 8c41040..7b40a35 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # PosInformatique.FluentAssertions.Json PosInformatique.FluentAssertions.Json is a library to assert JSON serialization using the *Fluent Assertions* library style coding. +[![Nuget](https://img.shields.io/nuget/v/PosInformatique.FluentAssertions.Json?label=PosInformatique.FluentAssertions.Json)](https://www.nuget.org/packages/PosInformatique.FluentAssertions.Json/) + ## Installing from NuGet The [PosInformatique.FluentAssertions.Json](https://www.nuget.org/packages/PosInformatique.FluentAssertions.Json/) library is available directly on the diff --git a/src/FluentAssertions.Json/FluentAssertions.Json.csproj b/src/FluentAssertions.Json/FluentAssertions.Json.csproj index bf34078..90d8225 100644 --- a/src/FluentAssertions.Json/FluentAssertions.Json.csproj +++ b/src/FluentAssertions.Json/FluentAssertions.Json.csproj @@ -11,6 +11,9 @@ https://github.com/PosInformatique/PosInformatique.FluentAssertions.Json README.md + 1.4.0 + - Add new overload BeJsonDeserializableInto() method to test if a Stream contains a JSON serializable object. + 1.3.0 - Add new overload BeJsonDeserializableInto() method to test the string collections. - Add new overload BeJsonDeserializableInto() method to test the string and numeric values. diff --git a/src/FluentAssertions.Json/JsonFluentAssertionsExtensions.cs b/src/FluentAssertions.Json/JsonFluentAssertionsExtensions.cs index 08a7b80..41e29f1 100644 --- a/src/FluentAssertions.Json/JsonFluentAssertionsExtensions.cs +++ b/src/FluentAssertions.Json/JsonFluentAssertionsExtensions.cs @@ -14,6 +14,7 @@ namespace FluentAssertions using FluentAssertions.Equivalency; using FluentAssertions.Numeric; using FluentAssertions.Primitives; + using FluentAssertions.Streams; using PosInformatique.FluentAssertions.Json; /// @@ -142,7 +143,7 @@ public static void BeJsonDeserializableInto(this StringCollectionAssertions a public static void BeJsonDeserializableInto(this NumericAssertions assertions, T expectedObject, JsonSerializerOptions? options = null) where T : struct, IComparable { - BeJsonDeserializableIntoCore(assertions.Subject, expectedObject, GetSerializerOptions(options)); + BeJsonDeserializableIntoCore(assertions.Subject!, expectedObject, GetSerializerOptions(options)); } /// @@ -159,6 +160,20 @@ public static void BeJsonDeserializableInto(this StringAssertions assertions, BeJsonDeserializableIntoCore(assertions.Subject, expectedObject, GetSerializerOptions(options)); } + /// + /// Check if the JSON subject stream is deserializable into the specified argument. + /// + /// Type of the object to deserialize from JSON. + /// which contains the JSON subject to deserialize. + /// Expected string value deserialized expected. + /// to use to assert the deserialization. If not specified + /// the default of the + /// will be used. + public static void BeJsonDeserializableInto(this StreamAssertions assertions, T expectedObject, JsonSerializerOptions? options = null) + { + BeJsonDeserializableIntoCore(assertions.Subject, expectedObject, GetSerializerOptions(options)); + } + /// /// Check if the JSON subject object is deserializable into the specified argument. /// @@ -226,7 +241,7 @@ public static void BeJsonDeserializableInto(this NumericAssertions asserti configureOptions(optionsCopy); - BeJsonDeserializableIntoCore(assertions.Subject, expectedObject, optionsCopy); + BeJsonDeserializableIntoCore(assertions.Subject!, expectedObject, optionsCopy); } /// @@ -277,11 +292,27 @@ private static void BeJsonSerializableIntoCore(ObjectAssertions assertion } } + private static void BeJsonDeserializableIntoCore(Stream subject, T expectedObject, JsonSerializerOptions options) + { + using var memoryStream = new MemoryStream(); + + subject.CopyTo(memoryStream); + + var deserializedObject = JsonSerializer.Deserialize(memoryStream.ToArray(), options); + + AreEquivalent(deserializedObject, expectedObject); + } + private static void BeJsonDeserializableIntoCore(object subject, T expectedObject, JsonSerializerOptions options) { var jsonText = JsonSerializer.Serialize(subject, options); var deserializedObject = JsonSerializer.Deserialize(jsonText, options); + AreEquivalent(deserializedObject, expectedObject); + } + + private static void AreEquivalent(T deserializedObject, T expectedObject) + { deserializedObject.Should().BeEquivalentTo(expectedObject, opt => { opt.Using(ctx => diff --git a/tests/FluentAssertions.Json.Tests/JsonFluentAssertionsExtensionsTest.cs b/tests/FluentAssertions.Json.Tests/JsonFluentAssertionsExtensionsTest.cs index b4258dd..ff03530 100644 --- a/tests/FluentAssertions.Json.Tests/JsonFluentAssertionsExtensionsTest.cs +++ b/tests/FluentAssertions.Json.Tests/JsonFluentAssertionsExtensionsTest.cs @@ -848,6 +848,106 @@ public void BeJsonDeserializableInto() json.Should().BeJsonDeserializableInto(expectedObject); } + [Fact] + public void BeJsonDeserializableInto_FromStream() + { + var json = new + { + string_property = "The string value", + int32_property = 1234, + boolean_property = true, + null_property = (string)null, + inner_object = new + { + inner_string_property = "Inner string value", + }, + collection_int = new[] + { + 10, + 20, + }, + collection_object = new[] + { + new + { + inner_string_property = "Inner object 1", + }, + new + { + inner_string_property = "Inner object 2", + }, + }, + }; + + using var stream = new MemoryStream(); + + JsonSerializer.Serialize(stream, json); + + stream.Position = 0; + + var expectedObject = new JsonSerializableClass() + { + StringProperty = "The string value", + Int32Property = 1234, + BooleanProperty = true, + NullProperty = null, + InnerObject = new JsonSerializableClassInnerObject() + { + InnerStringProperty = "Inner string value", + }, + CollectionInt32 = new List + { + 10, + 20, + }, + CollectionObjects = new List() + { + new JsonSerializableClassInnerObject() + { + InnerStringProperty = "Inner object 1", + }, + new JsonSerializableClassInnerObject() + { + InnerStringProperty = "Inner object 2", + }, + }, + }; + + stream.Should().BeJsonDeserializableInto(expectedObject); + } + + [Fact] + public void BeJsonDeserializableInto_FromStream_WithSpecificOptions() + { + var json = new + { + int32_property = 10, + enum_property = "B", + }; + + using var stream = new MemoryStream(); + + JsonSerializer.Serialize(stream, json); + + stream.Position = 0; + + var options = new JsonSerializerOptions() + { + Converters = + { + new JsonStringEnumConverter(), + }, + }; + + stream.Should().BeJsonDeserializableInto( + new JsonSerializableClassWithEnum() + { + Int32Property = 10, + EnumProperty = EnumTest.B, + }, + options); + } + [Fact] public void BeJsonDeserializableInto_WithSubjectNullAndExpectedNull() {