Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add overload to HaveElement() to be able to assert on occurrences for XDocument and XElement #1880

Merged
merged 47 commits into from Apr 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
0fe446b
Added BeSingle Assertion for xElement
Sep 17, 2021
d66b588
Implemented HaveCount Method for xElement
Sep 17, 2021
8edaa01
Improved comments and implementation
Sep 17, 2021
74f4f37
Added release notes and documentation
Sep 17, 2021
eee29f9
Verified API changes
Sep 17, 2021
e193366
Moved implementation to xDocument for better reading.
Nov 1, 2021
5163dc6
Updated method summary
Nov 1, 2021
3f75aa4
Updated documentation
Nov 1, 2021
4fcd882
ApiCheck
Nov 1, 2021
5da1e4e
typo
Nov 1, 2021
0cef179
Review
ITaluone Apr 5, 2022
6b41efc
Update PR link
ITaluone Apr 5, 2022
4d79a6f
Approve API
ITaluone Apr 5, 2022
1f97352
Use `OccurrenceConstraint` for asserting on counts
ITaluone Apr 6, 2022
57cdde1
Minor naming corrections
ITaluone Apr 6, 2022
b642a55
Minor error message improvements
ITaluone Apr 6, 2022
45bf499
Re-order tests
ITaluone Apr 6, 2022
0e58c84
Add line break
ITaluone Apr 6, 2022
87f5090
Minor comment improvement
ITaluone Apr 6, 2022
ec0ae7f
Fix `AndWhichConstraint` for multiple `XElement`
ITaluone Apr 6, 2022
53663a3
Add missing test
ITaluone Apr 6, 2022
5a9add8
Minor test naming improvement
ITaluone Apr 6, 2022
64630ef
Update release notes
ITaluone Apr 6, 2022
f713661
Update XML documentation
ITaluone Apr 6, 2022
f33c8b5
Fix some redundant XML documentation failures
ITaluone Apr 8, 2022
95a031f
Add missing overload for `HaveSingleElement()`
ITaluone Apr 8, 2022
fbc583d
Verify the API changes
ITaluone Apr 8, 2022
e652c2f
Remove literal names in test names
ITaluone Apr 8, 2022
d805e28
Add the `HaveSingleElement`, `HaveElement` to `XElementAssertions` as…
ITaluone Apr 8, 2022
2c501c6
Verify the API changes for `XElementAssertions`
ITaluone Apr 8, 2022
13443ee
Remove `HaveSingleElement` because it is redundant and less fluent
ITaluone Apr 19, 2022
657bc57
Accept API changes
ITaluone Apr 19, 2022
ca551e7
Add tests for using `AssertionScope`
ITaluone Apr 19, 2022
f5cab9f
Fix release notes
ITaluone Apr 19, 2022
8c62200
Approve API
ITaluone Apr 19, 2022
af054b6
Move `Guard` at the top of the method
ITaluone Apr 19, 2022
6041e4e
Add another test case for using `AssertionScope`
ITaluone Apr 19, 2022
fccddfe
Fix documentation
ITaluone Apr 19, 2022
bafed48
Fix API
ITaluone Apr 19, 2022
e86f05a
Add missing tests for `Guard`
ITaluone Apr 19, 2022
679f0ff
Re-phrase failure message to include occurrence mode
ITaluone Apr 19, 2022
a5085eb
Fix test names that are incorrect and/or awkward to read
ITaluone Apr 19, 2022
79387ba
Some minor optimizations
ITaluone Apr 20, 2022
8a8f930
Take care of some code style practices
ITaluone Apr 20, 2022
415fcbc
Merge branch 'develop' into issue-1681
ITaluone Apr 21, 2022
4a6f889
Rework the failure message to use the registered reportable
ITaluone Apr 22, 2022
55063f1
Fix xml documentation
ITaluone Apr 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
99 changes: 93 additions & 6 deletions Src/FluentAssertions/Xml/XDocumentAssertions.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml;
using System.Xml.Linq;

Expand Down Expand Up @@ -132,7 +134,7 @@ public AndConstraint<XDocumentAssertions> NotBeEquivalentTo(XDocument unexpected
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
"Cannot assert the document has a root element if the expected name is <null>*");
"Cannot assert the document has a root element if the expected name is <null>.");

return HaveRoot(XNamespace.None + expected, because, becauseArgs);
}
Expand All @@ -157,7 +159,7 @@ public AndConstraint<XDocumentAssertions> NotBeEquivalentTo(XDocument unexpected
}

Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
"Cannot assert the document has a root element if the expected name is <null>*");
"Cannot assert the document has a root element if the expected name is <null>.");

XElement root = Subject.Root;

Expand All @@ -176,7 +178,7 @@ public AndConstraint<XDocumentAssertions> NotBeEquivalentTo(XDocument unexpected
/// child element with the specified <paramref name="expected"/> name.
/// </summary>
/// <param name="expected">
/// The name of the expected child element of the current document's Root <see cref="XDocument.Root"/> element.
/// The name of the expected child element of the current document's <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
Expand All @@ -189,17 +191,43 @@ public AndConstraint<XDocumentAssertions> NotBeEquivalentTo(XDocument unexpected
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
"Cannot assert the document has an element if the expected name is <null>*");
"Cannot assert the document has an element if the expected name is <null>.");

return HaveElement(XNamespace.None + expected, because, becauseArgs);
}

/// <summary>
/// Asserts that the <see cref="XDocument.Root"/> element of the current <see cref="XDocument"/> has the specified occurrence of
/// child elements with the specified <paramref name="expected"/> name.
/// </summary>
/// <param name="expected">
/// The name of the expected child element of the current document's <see cref="XDocument.Root"/> element.
ITaluone marked this conversation as resolved.
Show resolved Hide resolved
/// </param>
/// <param name="occurrenceConstraint">
/// A constraint specifying the number of times the specified elements should appear.
/// </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 AndWhichConstraint<XDocumentAssertions, IEnumerable<XElement>> HaveElement(string expected,
OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
ITaluone marked this conversation as resolved.
Show resolved Hide resolved
"Cannot assert the document has an element if the expected name is <null>.");

return HaveElement(XNamespace.None + expected, occurrenceConstraint, because, becauseArgs);
}

/// <summary>
/// Asserts that the <see cref="XDocument.Root"/> element of the current <see cref="XDocument"/> has a direct
/// 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.
/// The full name <see cref="XName"/> of the expected child element of the current document's <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
Expand All @@ -217,7 +245,7 @@ public AndConstraint<XDocumentAssertions> NotBeEquivalentTo(XDocument unexpected
}

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

Execute.Assertion
.ForCondition(Subject.Root is not null)
Expand All @@ -237,6 +265,65 @@ public AndConstraint<XDocumentAssertions> NotBeEquivalentTo(XDocument unexpected
return new AndWhichConstraint<XDocumentAssertions, XElement>(this, xElement);
}

/// <summary>
/// Asserts that the <see cref="XDocument.Root"/> element of the current <see cref="XDocument"/> has the specified occurrence of
/// 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 <see cref="XDocument.Root"/> element.
/// </param>
/// <param name="occurrenceConstraint">
/// A constraint specifying the number of times the specified elements should appear.
/// </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 AndWhichConstraint<XDocumentAssertions, IEnumerable<XElement>> HaveElement(XName expected,
OccurrenceConstraint occurrenceConstraint, string because = "",
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
ITaluone marked this conversation as resolved.
Show resolved Hide resolved
"Cannot assert the document has an element count if the element name is <null>.");

bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Cannot assert the count if the document itself is <null>.");

IEnumerable<XElement> xElements = Enumerable.Empty<XElement>();

if (success)
{
var root = Subject.Root;
success = Execute.Assertion
.ForCondition(root is not null)
.BecauseOf(because, becauseArgs)
.FailWith(
ITaluone marked this conversation as resolved.
Show resolved Hide resolved
"Expected {context:subject} to have root element containing a child {0}{reason}, but it has no root element.",
expected.ToString());

if (success)
{
xElements = root.Elements(expected);
int actual = xElements.Count();

Execute.Assertion
.ForConstraint(occurrenceConstraint, actual)
.BecauseOf(because, becauseArgs)
.FailWith(
$"Expected {{context:subject}} to have a root element containing a child {{0}} " +
$"{{expectedOccurrence}}{{reason}}, but found it {actual.Times()}.",
expected.ToString());
}
}

return new AndWhichConstraint<XDocumentAssertions, IEnumerable<XElement>>(this, xElements);
}

/// <summary>
/// Returns the type of the subject the assertion applies on.
/// </summary>
Expand Down
78 changes: 78 additions & 0 deletions Src/FluentAssertions/Xml/XElementAssertions.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using FluentAssertions.Common;
Expand Down Expand Up @@ -288,6 +290,82 @@ public AndConstraint<XElementAssertions> HaveValue(string expected, string becau
return new AndWhichConstraint<XElementAssertions, XElement>(this, xElement);
}

/// <summary>
/// Asserts that the <see cref="XElement"/> of the current <see cref="XElement"/> has the specified occurrence of
/// 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 element's <see cref="XElement"/>.
/// </param>
/// <param name="occurrenceConstraint">
/// A constraint specifying the number of times the specified elements should appear.
/// </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 AndWhichConstraint<XElementAssertions, IEnumerable<XElement>> HaveElement(XName expected,
OccurrenceConstraint occurrenceConstraint, string because = "",
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
"Cannot assert the element has an element count if the element name is <null>.");

bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith(
"Expected {context:subject} to have an element with count of {0}{reason}, but the element itself is <null>.",
expected.ToString());

IEnumerable<XElement> xElements = Enumerable.Empty<XElement>();

if (success)
{
xElements = Subject.Elements(expected);
int actual = xElements.Count();

Execute.Assertion
.ForConstraint(occurrenceConstraint, actual)
.BecauseOf(because, becauseArgs)
.FailWith(
$"Expected {{context:subject}} to have an element {{0}} {{expectedOccurrence}}" +
$"{{reason}}, but found it {actual.Times()}.",
expected.ToString());
}

return new AndWhichConstraint<XElementAssertions, IEnumerable<XElement>>(this, xElements);
}

/// <summary>
/// Asserts that the <see cref="XElement"/> of the current <see cref="XElement"/> has the specified occurrence of
/// child elements with the specified <paramref name="expected"/> name.
/// </summary>
/// <param name="expected">
/// The name of the expected child element of the current element's <see cref="XElement"/>.
/// </param>
/// <param name="occurrenceConstraint">
/// A constraint specifying the number of times the specified elements should appear.
/// </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 AndWhichConstraint<XElementAssertions, IEnumerable<XElement>> HaveElement(string expected,
OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
"Cannot assert the element has an element if the expected name is <null>.");

return HaveElement(XNamespace.None + expected, occurrenceConstraint, because, becauseArgs);
}

/// <summary>
/// Returns the type of the subject the assertion applies on.
/// </summary>
Expand Down
Expand Up @@ -2685,6 +2685,8 @@ 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.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>> HaveElement(string expected, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XDocumentAssertions, System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>> HaveElement(System.Xml.Linq.XName expected, FluentAssertions.OccurrenceConstraint occurrenceConstraint, 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> NotBe(System.Xml.Linq.XDocument unexpected, string because = "", params object[] becauseArgs) { }
Expand All @@ -2700,6 +2702,8 @@ namespace FluentAssertions.Xml
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XElementAssertions> HaveAttribute(System.Xml.Linq.XName expectedName, string expectedValue, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XElementAssertions, System.Xml.Linq.XElement> HaveElement(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XElementAssertions, System.Xml.Linq.XElement> HaveElement(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XElementAssertions, System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>> HaveElement(string expected, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<FluentAssertions.Xml.XElementAssertions, System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>> HaveElement(System.Xml.Linq.XName expected, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XElementAssertions> HaveValue(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XElementAssertions> NotBe(System.Xml.Linq.XElement unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Xml.XElementAssertions> NotBeEquivalentTo(System.Xml.Linq.XElement unexpected, string because = "", params object[] becauseArgs) { }
Expand Down