Skip to content

Commit

Permalink
Add BeValidJson for checking strings (#29)
Browse files Browse the repository at this point in the history
* Add BeValidJson for checking strings

It also allows consecutive checks on the parsed JSON using the Which
property.

The check does not limit the valid values to objects and arrays because
of reasons explained here:
https://stackoverflow.com/questions/7487869/is-this-simple-string-considered-valid-json
  • Loading branch information
ronaldkroon authored and dennisdoomen committed Aug 30, 2019
1 parent 28404d9 commit 6958420
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 20 deletions.
3 changes: 2 additions & 1 deletion FluentAssertions.Json.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,5 @@ public void When_$scenario$_it_should_$behavior$()
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=012E3B0572DEF2448B0B5D9AA88E6210/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=012E3B0572DEF2448B0B5D9AA88E6210/Scope/=C3001E7C0DA78E4487072B7E050D86C5/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=012E3B0572DEF2448B0B5D9AA88E6210/Scope/=C3001E7C0DA78E4487072B7E050D86C5/Type/@EntryValue">InCSharpFile</s:String>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=012E3B0572DEF2448B0B5D9AA88E6210/Scope/=C3001E7C0DA78E4487072B7E050D86C5/CustomProperties/=minimumLanguageVersion/@EntryIndexedValue">2.0</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=012E3B0572DEF2448B0B5D9AA88E6210/Scope/=C3001E7C0DA78E4487072B7E050D86C5/CustomProperties/=minimumLanguageVersion/@EntryIndexedValue">2.0</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=jtoken/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
7 changes: 2 additions & 5 deletions Src/FluentAssertions.Json/JTokenAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ public JTokenAssertions(JToken subject)
params object[] becauseArgs)
{
Difference difference = JTokenDifferentiator.FindFirstDifference(Subject, expected);
JTokenFormatter formatter = new JTokenFormatter();

var message = $"Expected JSON document {Format(Subject, true).Replace("{", "{{").Replace("}", "}}")}" +
$" to be equivalent to {Format(expected, true).Replace("{", "{{").Replace("}", "}}")}" +
Expand Down Expand Up @@ -209,7 +208,7 @@ public AndConstraint<JTokenAssertions> MatchRegex(string regularExpression, stri
}

Execute.Assertion
.ForCondition(Regex.IsMatch(Subject.Value<string>(), regularExpression ?? ""))
.ForCondition(Regex.IsMatch(Subject.Value<string>(), regularExpression))
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:JSON property} {0} to match regex pattern {1}{reason}, but found {2}.",
Subject.Path, regularExpression, Subject.Value<string>());
Expand Down Expand Up @@ -314,7 +313,6 @@ public AndConstraint<JTokenAssertions> NotMatchRegex(string regularExpression, s
/// </param>
public AndWhichConstraint<JTokenAssertions, JToken> ContainSingleItem(string because = "", params object[] becauseArgs)
{
var formatter = new JTokenFormatter();
string formattedDocument = Format(Subject).Replace("{", "{{").Replace("}", "}}");

using (new AssertionScope("JSON document " + formattedDocument))
Expand All @@ -337,7 +335,6 @@ public AndConstraint<JTokenAssertions> NotMatchRegex(string regularExpression, s
/// </param>
public AndConstraint<JTokenAssertions> HaveCount(int expected, string because = "", params object[] becauseArgs)
{
var formatter = new JTokenFormatter();
string formattedDocument = Format(Subject).Replace("{", "{{").Replace("}", "}}");

using (new AssertionScope("JSON document " + formattedDocument))
Expand Down Expand Up @@ -452,7 +449,7 @@ private bool JTokenContainsSubtree(JToken token, JToken subtree)

}
}

public string Format(JToken value, bool useLineBreaks = false)
{
return new JTokenFormatter().Format(value, new FormattingContext
Expand Down
31 changes: 31 additions & 0 deletions Src/FluentAssertions.Json/StringAssertionsExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using FluentAssertions.Execution;
using FluentAssertions.Primitives;
using Newtonsoft.Json.Linq;

namespace FluentAssertions.Json
{
public static class StringAssertionsExtensions
{
[CustomAssertionAttribute]
public static AndWhichConstraint<StringAssertions, JToken> BeValidJson(
this StringAssertions stringAssertions,
string because = "",
params object[] becauseArgs)
{
JToken json = null;

try
{
json = JToken.Parse(stringAssertions.Subject);
}
catch (Exception ex)
{
Execute.Assertion.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:string} to be valid JSON{reason}, but parsing failed with {0}.", ex.Message);
}

return new AndWhichConstraint<StringAssertions, JToken>(stringAssertions, json);
}
}
}
24 changes: 10 additions & 14 deletions Tests/FluentAssertions.Json.Shared.Specs/JTokenAssertionsSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@
namespace FluentAssertions.Json
{
// ReSharper disable InconsistentNaming
// ReSharper disable ExpressionIsAlwaysNull
public class JTokenAssertionsSpecs
{

private static readonly JTokenFormatter _formatter = new JTokenFormatter();

#region (Not)BeEquivalentTo

[Fact]
Expand Down Expand Up @@ -642,7 +640,7 @@ public void When_jtoken_is_null_ContainSingleItem_should_fail()
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>()
.WithMessage($"Expected JSON document <null> to contain a single item because null is not allowed, but found <null>.");
.WithMessage("Expected JSON document <null> to contain a single item because null is not allowed, but found <null>.");
}

[Fact]
Expand All @@ -662,7 +660,7 @@ public void When_jtoken_is_an_empty_object_ContainSingleItem_should_fail()
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>()
.WithMessage($"Expected JSON document * to contain a single item because less is not allowed, but the collection is empty.");
.WithMessage("Expected JSON document * to contain a single item because less is not allowed, but the collection is empty.");
}

[Fact]
Expand All @@ -681,10 +679,8 @@ public void When_jtoken_has_multiple_elements_ContainSingleItem_should_fail()
//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
string formattedSubject = Format(subject);

act.Should().Throw<XunitException>()
.WithMessage($"Expected JSON document*id*42*admin*true*to contain a single item because more is not allowed, but found*");
.WithMessage("Expected JSON document*id*42*admin*true*to contain a single item because more is not allowed, but found*");
}

[Fact]
Expand Down Expand Up @@ -742,7 +738,7 @@ public void When_jtoken_is_an_empty_array_ContainSingleItem_should_fail()
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>()
.WithMessage($"Expected JSON document [] to contain a single item because less is not allowed, but the collection is empty.");
.WithMessage("Expected JSON document [] to contain a single item because less is not allowed, but the collection is empty.");
}

[Fact]
Expand Down Expand Up @@ -826,7 +822,7 @@ public void When_jtoken_is_null_HaveCount_should_fail()
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>()
.WithMessage($"Expected JSON document <null> to contain 1 item(s) because null is not allowed, but found <null>.");
.WithMessage("Expected JSON document <null> to contain 1 item(s) because null is not allowed, but found <null>.");
}

[Fact]
Expand All @@ -846,7 +842,7 @@ public void When_expecting_a_different_number_of_elements_than_the_actual_number
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>()
.WithMessage($"Expected JSON document * to contain 1 item(s) because numbers matter, but found 0.");
.WithMessage("Expected JSON document * to contain 1 item(s) because numbers matter, but found 0.");
}

[Fact]
Expand Down Expand Up @@ -885,7 +881,7 @@ public void When_expecting_a_different_number_of_array_items_than_the_actual_num
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>()
.WithMessage($"Expected JSON document * to contain 3 item(s) because the more the better, but found 2.");
.WithMessage("Expected JSON document * to contain 3 item(s) because the more the better, but found 2.");
}

#endregion HaveCount
Expand Down Expand Up @@ -1026,13 +1022,13 @@ public void When_property_types_dont_match_ContainSubtree_should_fail()
}

#endregion
public static string Format(JToken value, bool useLineBreaks = false)

private static string Format(JToken value, bool useLineBreaks = false)
{
return new JTokenFormatter().Format(value, new FormattingContext
{
UseLineBreaks = useLineBreaks
}, null);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<Compile Include="$(MSBuildThisFileDirectory)JsonAssertionExtensionsSpecs.cs" />
<Compile Include="$(MSBuildThisFileDirectory)JTokenAssertionsSpecs.cs" />
<Compile Include="$(MSBuildThisFileDirectory)JTokenFormatterSpecs.cs" />
<Compile Include="$(MSBuildThisFileDirectory)StringAssertionsExtensionsSpecs.cs" />
<Compile Include="$(MSBuildThisFileDirectory)StringExtensions.cs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System;
using Newtonsoft.Json.Linq;
using Xunit;
using Xunit.Sdk;

namespace FluentAssertions.Json.Net45.Specs
{
// ReSharper disable ExpressionIsAlwaysNull
public class StringAssertionsExtensionsSpecs
{
#region BeValidJson

[Fact]
public void When_checking_valid_json_BeValidJson_should_succeed()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
string subject = "{ id: 42, admin: true }";

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () => subject.Should().BeValidJson();

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().NotThrow();
}

[Fact]
public void When_checking_valid_json_BeValidJson_should_enable_consecutive_jtoken_assertions()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
string subject = "{ id: 42 }";

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
object which = subject.Should().BeValidJson().Which;

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
which.Should().BeAssignableTo<JToken>();
}

[Fact]
public void When_checking_null_BeValidJson_should_fail()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
string subject = null;
Exception caughtException = null;

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
try
{
subject.Should().BeValidJson("null is not allowed");
}
catch (Exception ex)
{
caughtException = ex;
}

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
caughtException.Should()
.BeOfType<XunitException>()
.Which.Message.Should()
.Match("Expected subject to be valid JSON because null is not allowed, but parsing failed with \"*\".");
}

[Fact]
public void When_checking_invalid_json_BeValidJson_should_fail()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
string subject = "invalid json";
Exception caughtException = null;

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
try
{
subject.Should().BeValidJson("we like {0}", "JSON");
}
catch (Exception ex)
{
caughtException = ex;
}

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
caughtException.Should()
.BeOfType<XunitException>()
.Which.Message.Should()
.Match("Expected subject to be valid JSON because we like JSON, but parsing failed with \"*\".");
}

#endregion

}
}

0 comments on commit 6958420

Please sign in to comment.