Skip to content

Commit c68ced3

Browse files
author
Ronald Kroon
committed
Add ContainSingleItem assertion to JToken
1 parent ec07639 commit c68ced3

File tree

3 files changed

+209
-1
lines changed

3 files changed

+209
-1
lines changed

Src/FluentAssertions.Json.Shared/JTokenAssertions.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Diagnostics;
2+
using FluentAssertions.Collections;
23
using FluentAssertions.Common;
34
using FluentAssertions.Execution;
45
using FluentAssertions.Formatting;
@@ -13,6 +14,8 @@ namespace FluentAssertions.Json
1314
[DebuggerNonUserCode]
1415
public class JTokenAssertions : ReferenceTypeAssertions<JToken, JTokenAssertions>
1516
{
17+
private GenericCollectionAssertions<JToken> EnumerableSubject { get; }
18+
1619
static JTokenAssertions()
1720
{
1821
Formatter.AddFormatter(new JTokenFormatter());
@@ -25,6 +28,7 @@ static JTokenAssertions()
2528
public JTokenAssertions(JToken subject)
2629
{
2730
Subject = subject;
31+
EnumerableSubject = new GenericCollectionAssertions<JToken>(subject);
2832
}
2933

3034
/// <summary>
@@ -287,5 +291,27 @@ public AndWhichConstraint<JTokenAssertions, JToken> NotHaveElement(string unexpe
287291

288292
return new AndWhichConstraint<JTokenAssertions, JToken>(this, jToken);
289293
}
294+
295+
/// <summary>
296+
/// Expects the current <see cref="JToken" /> to contain only a single item.
297+
/// </summary>
298+
/// <param name="because">
299+
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
300+
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
301+
/// </param>
302+
/// <param name="becauseArgs">
303+
/// Zero or more objects to format using the placeholders in <see cref="because" />.
304+
/// </param>
305+
public AndWhichConstraint<JTokenAssertions, JToken> ContainSingleItem(string because = "", params object[] becauseArgs)
306+
{
307+
var formatter = new JTokenFormatter();
308+
string formattedDocument = formatter.ToString(Subject).Replace("{", "{{").Replace("}", "}}");
309+
310+
using (new AssertionScope("JSON document " + formattedDocument))
311+
{
312+
var constraint = EnumerableSubject.ContainSingle(because, becauseArgs);
313+
return new AndWhichConstraint<JTokenAssertions, JToken>(this, constraint.Which);
314+
}
315+
}
290316
}
291317
}

Src/FluentAssertions.Json.Shared/JTokenFormatter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ public string ToString(object value, bool useLineBreaks = false, IList<object> p
4141
int nestedPropertyLevel = 0)
4242
{
4343
var jToken = (JToken)value;
44-
return useLineBreaks ? jToken.ToString(Newtonsoft.Json.Formatting.Indented) : jToken.ToString().RemoveNewLines();
44+
string result = useLineBreaks ? jToken?.ToString(Newtonsoft.Json.Formatting.Indented) : jToken?.ToString().RemoveNewLines();
45+
return result ?? "<null>";
4546
}
4647
}
4748
}

Tests/FluentAssertions.Json.Shared.Specs/JTokenAssertionsSpecs.cs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using Microsoft.VisualStudio.TestTools.UnitTesting;
34
using Newtonsoft.Json.Linq;
@@ -352,5 +353,185 @@ public void When_jtoken_does_have_element_NotHaveElement_should_fail()
352353
.ShouldThrow<AssertFailedException>()
353354
.WithMessage($"Did not expect JSON document {_formatter.ToString(subject)} to have element \"id\" because foo.");
354355
}
356+
357+
[TestMethod]
358+
public void When_jtoken_has_a_single_element_ContainSingleItem_should_succeed()
359+
{
360+
//-----------------------------------------------------------------------------------------------------------
361+
// Arrange
362+
//-----------------------------------------------------------------------------------------------------------
363+
var subject = JToken.Parse("{ id: 42 }");
364+
365+
//-----------------------------------------------------------------------------------------------------------
366+
// Act
367+
//-----------------------------------------------------------------------------------------------------------
368+
Action act = () => subject.Should().ContainSingleItem();
369+
370+
//-----------------------------------------------------------------------------------------------------------
371+
// Assert
372+
//-----------------------------------------------------------------------------------------------------------
373+
act.ShouldNotThrow();
374+
}
375+
376+
[TestMethod]
377+
public void When_jtoken_has_a_single_element_ContainSingleItem_should_return_which_element_it_is()
378+
{
379+
//-----------------------------------------------------------------------------------------------------------
380+
// Arrange
381+
//-----------------------------------------------------------------------------------------------------------
382+
var subject = JToken.Parse("{ id: 42 }");
383+
384+
//-----------------------------------------------------------------------------------------------------------
385+
// Act
386+
//-----------------------------------------------------------------------------------------------------------
387+
var element = subject.Should().ContainSingleItem().Which;
388+
389+
//-----------------------------------------------------------------------------------------------------------
390+
// Assert
391+
//-----------------------------------------------------------------------------------------------------------
392+
element.Should().Be(new JProperty("id", 42));
393+
}
394+
395+
[TestMethod]
396+
public void When_jtoken_is_null_ContainSingleItem_should_fail()
397+
{
398+
//-----------------------------------------------------------------------------------------------------------
399+
// Arrange
400+
//-----------------------------------------------------------------------------------------------------------
401+
JToken subject = null;
402+
403+
//-----------------------------------------------------------------------------------------------------------
404+
// Act
405+
//-----------------------------------------------------------------------------------------------------------
406+
Action act = () => subject.Should().ContainSingleItem("null is not allowed");
407+
408+
//-----------------------------------------------------------------------------------------------------------
409+
// Assert
410+
//-----------------------------------------------------------------------------------------------------------
411+
act.ShouldThrow<AssertFailedException>()
412+
.WithMessage($"Expected JSON document <null> to contain a single item because null is not allowed, but found <null>.");
413+
}
414+
415+
[TestMethod]
416+
public void When_jtoken_is_an_empty_object_ContainSingleItem_should_fail()
417+
{
418+
//-----------------------------------------------------------------------------------------------------------
419+
// Arrange
420+
//-----------------------------------------------------------------------------------------------------------
421+
var subject = JToken.Parse("{ }");
422+
423+
//-----------------------------------------------------------------------------------------------------------
424+
// Act
425+
//-----------------------------------------------------------------------------------------------------------
426+
Action act = () => subject.Should().ContainSingleItem("less is not allowed");
427+
428+
//-----------------------------------------------------------------------------------------------------------
429+
// Assert
430+
//-----------------------------------------------------------------------------------------------------------
431+
act.ShouldThrow<AssertFailedException>()
432+
.WithMessage($"Expected JSON document * to contain a single item because less is not allowed, but the collection is empty.");
433+
}
434+
435+
[TestMethod]
436+
public void When_jtoken_has_multiple_elements_ContainSingleItem_should_fail()
437+
{
438+
//-----------------------------------------------------------------------------------------------------------
439+
// Arrange
440+
//-----------------------------------------------------------------------------------------------------------
441+
var subject = JToken.Parse("{ id: 42, admin: true }");
442+
443+
//-----------------------------------------------------------------------------------------------------------
444+
// Act
445+
//-----------------------------------------------------------------------------------------------------------
446+
Action act = () => subject.Should().ContainSingleItem("more is not allowed");
447+
448+
//-----------------------------------------------------------------------------------------------------------
449+
// Assert
450+
//-----------------------------------------------------------------------------------------------------------
451+
string formattedSubject = _formatter.ToString(subject);
452+
453+
act.ShouldThrow<AssertFailedException>()
454+
.WithMessage($"Expected JSON document {formattedSubject} to contain a single item because more is not allowed, but found {formattedSubject}.");
455+
}
456+
457+
[TestMethod]
458+
public void When_jtoken_is_array_with_a_single_item_ContainSingleItem_should_succeed()
459+
{
460+
//-----------------------------------------------------------------------------------------------------------
461+
// Arrange
462+
//-----------------------------------------------------------------------------------------------------------
463+
var subject = JToken.Parse("[{ id: 42 }]");
464+
465+
//-----------------------------------------------------------------------------------------------------------
466+
// Act
467+
//-----------------------------------------------------------------------------------------------------------
468+
Action act = () => subject.Should().ContainSingleItem();
469+
470+
//-----------------------------------------------------------------------------------------------------------
471+
// Assert
472+
//-----------------------------------------------------------------------------------------------------------
473+
act.ShouldNotThrow();
474+
}
475+
476+
[TestMethod]
477+
public void When_jtoken_is_an_array_with_a_single_item_ContainSingleItem_should_return_which_element_it_is()
478+
{
479+
//-----------------------------------------------------------------------------------------------------------
480+
// Arrange
481+
//-----------------------------------------------------------------------------------------------------------
482+
var subject = JToken.Parse("[{ id: 42 }]");
483+
484+
//-----------------------------------------------------------------------------------------------------------
485+
// Act
486+
//-----------------------------------------------------------------------------------------------------------
487+
var element = subject.Should().ContainSingleItem().Which;
488+
489+
//-----------------------------------------------------------------------------------------------------------
490+
// Assert
491+
//-----------------------------------------------------------------------------------------------------------
492+
element.Should().Be(JToken.Parse("{ id: 42 }"));
493+
}
494+
495+
[TestMethod]
496+
public void When_jtoken_is_an_empty_array_ContainSingleItem_should_fail()
497+
{
498+
//-----------------------------------------------------------------------------------------------------------
499+
// Arrange
500+
//-----------------------------------------------------------------------------------------------------------
501+
var subject = JToken.Parse("[]");
502+
503+
//-----------------------------------------------------------------------------------------------------------
504+
// Act
505+
//-----------------------------------------------------------------------------------------------------------
506+
Action act = () => subject.Should().ContainSingleItem("less is not allowed");
507+
508+
//-----------------------------------------------------------------------------------------------------------
509+
// Assert
510+
//-----------------------------------------------------------------------------------------------------------
511+
act.ShouldThrow<AssertFailedException>()
512+
.WithMessage($"Expected JSON document [] to contain a single item because less is not allowed, but the collection is empty.");
513+
}
514+
515+
[TestMethod]
516+
public void When_jtoken_is_an_array_with_multiple_items_ContainSingleItem_should_fail()
517+
{
518+
//-----------------------------------------------------------------------------------------------------------
519+
// Arrange
520+
//-----------------------------------------------------------------------------------------------------------
521+
var subject = JToken.Parse("[1, 2]");
522+
523+
//-----------------------------------------------------------------------------------------------------------
524+
// Act
525+
//-----------------------------------------------------------------------------------------------------------
526+
Action act = () => subject.Should().ContainSingleItem("more is not allowed");
527+
528+
//-----------------------------------------------------------------------------------------------------------
529+
// Assert
530+
//-----------------------------------------------------------------------------------------------------------
531+
string formattedSubject = _formatter.ToString(subject);
532+
533+
act.ShouldThrow<AssertFailedException>()
534+
.WithMessage($"Expected JSON document {formattedSubject} to contain a single item because more is not allowed, but found {formattedSubject}.");
535+
}
355536
}
356537
}

0 commit comments

Comments
 (0)