Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions Src/FluentAssertions/Xml/XDocumentAssertions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Xml;
using System.Xml.Linq;

Expand Down Expand Up @@ -237,6 +238,71 @@ public AndWhichConstraint<XDocumentAssertions, XElement> HaveElement(XName expec
return new AndWhichConstraint<XDocumentAssertions, XElement>(this, xElement);
}

/// <summary>
/// Asserts that the <see cref="XDocument.Root"/> element of the current <see cref="XDocument"/> has a single
/// child element with the specified <paramref name="expected"/> name.
/// </summary>
/// <param name="expected">
/// The full name <see cref="XName"/> of the expected child element of the current document's Root <see cref="XDocument.Root"/> element.
/// </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.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public AndConstraint<XDocumentAssertions> HaveSingleElement(XName expected, string because = "", params object[] becauseArgs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should return a AndWhichConstraint<XDocumentAssertions, XElement> like HaveElement.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And use the Which that AndWhichConstraint exposes in your test

{
return HaveElementCount(expected, 1, because, becauseArgs);
}

/// <summary>
/// Asserts that the <see cref="XDocument.Root"/> element of the current <see cref="XDocument"/> has a the specified <paramref name="count"/> of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a typo around "has a the specified"

/// child elements with the specified <paramref name="expected"/> name.
/// </summary>
/// <param name="expected">
/// The full name <see cref="XName"/> of the expected child element of the current document's Root <see cref="XDocument.Root"/> element.
/// </param>
/// <param name="count">The expected count of elements in the document.</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.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public AndConstraint<XDocumentAssertions> HaveElementCount(XName expected, int count, string because = "",
params object[] becauseArgs)
{
if (Subject is null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The subject being null is actually something that could happen in a failing test, so I think you should use the normal ForCondition logic.

{
throw new InvalidOperationException("Cannot assert the count if the document itself is <null>.");
}

Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
"Cannot assert the document has an element count if the element name is <null>*");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the trailing * be a .?


Execute.Assertion
.ForCondition(Subject.Root is not null)
.BecauseOf(because, becauseArgs)
.FailWith(
"Expected {context:subject} to have root element with child {0}{reason}, but it has no root element.",
expected.ToString());

var xElements = Subject.Root.Elements(expected);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If HaveElementCount is called inside an AssertionScope the assertion above above will not fail immediately, so Subject.Root can be null here.
See e.g. XElementAssertions.HaveValue for an example of how to handle this.

int actualCount = xElements.Count();

Execute.Assertion
.ForCondition(actualCount == count)
.BecauseOf(because, becauseArgs)
.FailWith(
"Expected {context:subject} to have {0} child element(s) {1}{reason}, but found {2}.",
count, expected.ToString(), actualCount);

return new AndConstraint<XDocumentAssertions>(this);
}

/// <summary>
/// Returns the type of the subject the assertion applies on.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2579,8 +2579,10 @@ namespace FluentAssertions.Xml
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> BeEquivalentTo(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveElement(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveElement(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> HaveElementCount(System.Xml.Linq.XName expected, int count, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveRoot(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveRoot(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> HaveSingleElement(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> NotBe(System.Xml.Linq.XDocument unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> NotBeEquivalentTo(System.Xml.Linq.XDocument unexpected, string because = "", params object[] becauseArgs) { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2581,8 +2581,10 @@ namespace FluentAssertions.Xml
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> BeEquivalentTo(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveElement(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveElement(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> HaveElementCount(System.Xml.Linq.XName expected, int count, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveRoot(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveRoot(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> HaveSingleElement(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> NotBe(System.Xml.Linq.XDocument unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> NotBeEquivalentTo(System.Xml.Linq.XDocument unexpected, string because = "", params object[] becauseArgs) { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2581,8 +2581,10 @@ namespace FluentAssertions.Xml
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> BeEquivalentTo(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveElement(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveElement(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> HaveElementCount(System.Xml.Linq.XName expected, int count, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveRoot(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveRoot(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> HaveSingleElement(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> NotBe(System.Xml.Linq.XDocument unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> NotBeEquivalentTo(System.Xml.Linq.XDocument unexpected, string because = "", params object[] becauseArgs) { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2532,8 +2532,10 @@ namespace FluentAssertions.Xml
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> BeEquivalentTo(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveElement(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveElement(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> HaveElementCount(System.Xml.Linq.XName expected, int count, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveRoot(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveRoot(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> HaveSingleElement(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> NotBe(System.Xml.Linq.XDocument unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> NotBeEquivalentTo(System.Xml.Linq.XDocument unexpected, string because = "", params object[] becauseArgs) { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2581,8 +2581,10 @@ namespace FluentAssertions.Xml
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> BeEquivalentTo(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveElement(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveElement(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> HaveElementCount(System.Xml.Linq.XName expected, int count, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveRoot(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Xml.Linq.XElement> HaveRoot(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> HaveSingleElement(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> NotBe(System.Xml.Linq.XDocument unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XDocumentAssertions> NotBeEquivalentTo(System.Xml.Linq.XDocument unexpected, string because = "", params object[] becauseArgs) { }
}
Expand Down
100 changes: 100 additions & 0 deletions Tests/FluentAssertions.Specs/Xml/XDocumentAssertionSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1111,5 +1111,105 @@ public void When_asserting_a_document_has_a_null_element_it_should_fail()
}

#endregion

#region HaveSingleElement

[Fact]
public void When_asserting_document_has_a_single_child_element_and_it_does_it_should_succeed()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 I suggest to keep the name more functional and concise: A_single_expected_child_element_with_the_specified_name_should_be_accepted

{
// Arrange
var document = XDocument.Parse(
@"<parent>
<child />
</parent>");

// Act / Assert
document.Should().HaveSingleElement("child");
}

[Fact]
public void When_asserting_document_has_single_child_element_but_it_does_have_two_it_should_fail()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 Suggestion: Multiple_child_elements_with_the_specified_name_should_fail_the_test

{
// Arrange
var document = XDocument.Parse(
@"<parent>
<child />
<child />
</parent>");

// Act
Action act = () => document.Should().HaveSingleElement("child");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also validate that because and becauseArgs are properly passed along in this test.
See e.g. XDocumentAssertionSpecs.When_the_expected_element_is_null_it_fails


// Assert
act.Should().Throw<XunitException>().WithMessage(
"Expected document to have 1 child element(s) \"child\", but found 2.");
}

[Fact]
public void When_asserting_a_null_xDocument_to_have_a_single_element_it_should_fail()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 Suggestion: Cannot_ensure_a_single_element_if_the_document_is_null

{
// Arrange
XDocument xDocument = null;

// Act
Action act = () => xDocument.Should().HaveSingleElement("child");

// Assert
act.Should().Throw<InvalidOperationException>().WithMessage(
"Cannot assert the count if the document itself is <null>.");
}

#endregion

#region HaveElementCount

[Fact]
public void When_asserting_document_has_two_child_elements_and_it_does_it_should_succeed()
{
// Arrange
var document = XDocument.Parse(
@"<parent>
<child />
<child />
</parent>");

// Act / Assert
document.Should().HaveElementCount("child", 2);
}

[Fact]
public void When_asserting_document_has_two_child_elements_but_it_does_have_three_it_should_fail()
{
// Arrange
var document = XDocument.Parse(
@"<parent>
<child />
<child />
<child />
</parent>");

// Act
Action act = () => document.Should().HaveElementCount("child", 2);

// Assert
act.Should().Throw<XunitException>().WithMessage(
"Expected document to have 2 child element(s) \"child\", but found 3.");
}

[Fact]
public void When_asserting_a_null_xDocument_to_have_a_element_count_it_should_fail()
{
// Arrange
XDocument xDocument = null;

// Act
Action act = () => xDocument.Should().HaveElementCount("child", 1);

// Assert
act.Should().Throw<InvalidOperationException>().WithMessage(
"Cannot assert the count if the document itself is <null>.");
}

#endregion
}
}
4 changes: 3 additions & 1 deletion docs/_pages/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ sidebar:
* Adding new overloads to all `GreaterOrEqualTo` and `LessOrEqualTo` assertions, adding the word `Than` - [#1673](https://github.com/fluentassertions/fluentassertions/pull/1673)
* `BeAsync()` and `NotBeAsync()` are now also available on `MethodInfoSelectorAssertions` - [#1700](https://github.com/fluentassertions/fluentassertions/pull/1700)

* Added `HaveSingleElement` and `HaveElementCount` for `XDocument` - [#1681](https://github.com/fluentassertions/fluentassertions/pull/1684)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Added `HaveSingleElement` and `HaveElementCount` for `XDocument` - [#1681](https://github.com/fluentassertions/fluentassertions/pull/1684)
* Added `HaveSingleElement` and `HaveElementCount` for `XDocument` - [#1684](https://github.com/fluentassertions/fluentassertions/pull/1684)


### Fixes

* Prevent exceptions when asserting on `ImmutableArray<T>` - [#1668](https://github.com/fluentassertions/fluentassertions/pull/1668)
* `At` now retains the `DateTimeKind` and keeps sub-second precision when using a `TimeSpan` - [#1687](https://github.com/fluentassertions/fluentassertions/pull/1687).
* Removed iteration over enumerable when generating the `BeEmpty` assertion failure message - [#1692](https://github.com/fluentassertions/fluentassertions/pull/1692).
* Prevent `ArgumentNullException` when formatting a lambda expression containing an extension method - [#1696](https://github.com/fluentassertions/fluentassertions/pull/1696)
* `IgnoringCyclicReferences` in `BeEquivalentTo` now works while comparing value types using `ComparingByMembers` - [#1708](https://github.com/fluentassertions/fluentassertions/pull/1708)
* `IgnoringCyclicReferences` in `BeEquivalentTo` now works while comparing value types using `ComparingByMembers` - [#1708](https://github.com/fluentassertions/fluentassertions/pull/1708)
* Using `BeEquivalentTo` on a collection with nested collections would complain about missing members - [#1713](https://github.com/fluentassertions/fluentassertions/pull/1713)
* Formatting a lambda expression containing lifted operators - [#1714](https://github.com/fluentassertions/fluentassertions/pull/1714).
* Performance improvements in `BeEquivalentTo` by caching expensive Reflection operations - [#1719](https://github.com/fluentassertions/fluentassertions/pull/1719)
Expand Down
2 changes: 2 additions & 0 deletions docs/_pages/xml.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Fluent Assertions has support for assertions on several of the LINQ-to-XML class
```csharp
xDocument.Should().HaveRoot("configuration");
xDocument.Should().HaveElement("settings");
xDocument.Should().HaveSingleElement("settings");
xDocument.Should().HaveElementCount("settings", 1);

xElement.Should().HaveValue("36");
xElement.Should().HaveAttribute("age", "36");
Expand Down