Skip to content

Commit 7203f29

Browse files
author
Sam Segers
committed
add ContainSubtree that takes a config
1 parent 0606d9a commit 7203f29

2 files changed

Lines changed: 65 additions & 1 deletion

File tree

Src/FluentAssertions.Json/JTokenAssertions.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ static JTokenAssertions()
2727
/// Initializes a new instance of the <see cref="JTokenAssertions" /> class.
2828
/// </summary>
2929
/// <param name="subject">The subject</param>
30-
/// <param name="orCreate"></param>
30+
/// <param name="assertionChain">The assertion chain</param>
3131
public JTokenAssertions(JToken subject, AssertionChain assertionChain)
3232
: base(subject, assertionChain)
3333
{
@@ -496,6 +496,41 @@ public AndConstraint<JTokenAssertions> ContainSubtree(JToken subtree, string bec
496496
return BeEquivalentTo(subtree, true, options => options, because, becauseArgs);
497497
}
498498

499+
/// <summary>
500+
/// Recursively asserts that the current <see cref="JToken"/> contains at least the properties or elements of the specified <paramref name="subtree"/>.
501+
/// </summary>
502+
/// <param name="subtree">The subtree to search for</param>
503+
/// <param name="config">The options to consider while asserting values</param>
504+
/// <param name="because">
505+
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
506+
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
507+
/// </param>
508+
/// <param name="becauseArgs">
509+
/// Zero or more objects to format using the placeholders in <see cref="because" />.
510+
/// </param>
511+
/// <remarks>Use this method to match the current <see cref="JToken"/> against an arbitrary subtree,
512+
/// permitting it to contain any additional properties or elements. This way we can test multiple properties on a <see cref="JObject"/> at once,
513+
/// or test if a <see cref="JArray"/> contains any items that match a set of properties, assert that a JSON document has a given shape, etc. </remarks>
514+
/// <example>
515+
/// This example asserts the values of multiple properties of a child object within a JSON document.
516+
/// <code>
517+
/// var json = JToken.Parse("{ success: true, data: { id: 123, type: 'my-type', name: 'Noone' } }");
518+
/// json.Should().ContainSubtree(JToken.Parse("{ success: true, data: { type: 'my-type', name: 'Noone' } }"));
519+
/// </code>
520+
/// </example>
521+
/// <example>This example asserts that a <see cref="JArray"/> within a <see cref="JObject"/> has at least one element with at least the given properties</example>
522+
/// <code>
523+
/// var json = JToken.Parse("{ id: 1, items: [ { id: 2, type: 'my-type', name: 'Alpha' }, { id: 3, type: 'other-type', name: 'Bravo' } ] }");
524+
/// json.Should().ContainSubtree(JToken.Parse("{ items: [ { type: 'my-type', name: 'Alpha' } ] }"));
525+
/// </code>
526+
public AndConstraint<JTokenAssertions> ContainSubtree(JToken subtree,
527+
Func<IJsonAssertionOptions<object>, IJsonAssertionOptions<object>> config,
528+
string because = "",
529+
params object[] becauseArgs)
530+
{
531+
return BeEquivalentTo(subtree, true, config, because, becauseArgs);
532+
}
533+
499534
#pragma warning disable CA1822 // Making this method static is a breaking chan
500535
public string Format(JToken value, bool useLineBreaks = false)
501536
{

Tests/FluentAssertions.Json.Specs/JTokenAssertionsSpecs.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,35 @@ public void When_checking_subtree_with_an_invalid_expected_string_it_should_prov
966966
.WithInnerException<JsonReaderException>();
967967
}
968968

969+
[Fact]
970+
public void When_a_float_is_within_approximation_ContainSubtree_check_should_succeed()
971+
{
972+
// Arrange
973+
var actual = JToken.Parse("{ \"id\": 1.1232 }");
974+
var expected = JToken.Parse("{ \"id\": 1.1235 }");
975+
976+
// Act & Assert
977+
actual.Should().ContainSubtree(expected, options => options
978+
.Using<double>(d => d.Subject.Should().BeApproximately(d.Expectation, 1e-3))
979+
.WhenTypeIs<double>());
980+
}
981+
982+
[Fact]
983+
public void When_a_float_is_not_within_approximation_ContainSubtree_check_should_throw()
984+
{
985+
// Arrange
986+
var actual = JToken.Parse("{ \"id\": 1.1232 }");
987+
var expected = JToken.Parse("{ \"id\": 1.1235 }");
988+
989+
// Act & Assert
990+
actual.Should().
991+
Invoking(x => x.ContainSubtree(expected, options => options
992+
.Using<double>(d => d.Subject.Should().BeApproximately(d.Expectation, 1e-5))
993+
.WhenTypeIs<double>()))
994+
.Should().Throw<XunitException>()
995+
.WithMessage("JSON document has a different value at $.id.*");
996+
}
997+
969998
#endregion
970999

9711000
private static string Format(JToken value, bool useLineBreaks = false)

0 commit comments

Comments
 (0)