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

Added a new overload to MatchRegex() to assert on the number of regex matches #1869

Merged
merged 15 commits into from Apr 1, 2022
Merged
28 changes: 27 additions & 1 deletion Src/FluentAssertions/Primitives/StringAssertions.cs
Expand Up @@ -387,6 +387,16 @@ public AndConstraint<TAssertions> NotMatch(string wildcardPattern, string becaus
return new AndConstraint<TAssertions>((TAssertions)this);
}

public AndConstraint<TAssertions> MatchRegex([RegexPattern][StringSyntax("Regex")] string regularExpression,
OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs)
{
IT-VBFK marked this conversation as resolved.
Show resolved Hide resolved
MatchRegex(regularExpression, because, becauseArgs);
IT-VBFK marked this conversation as resolved.
Show resolved Hide resolved

MatchRegex(new Regex(regularExpression), occurrenceConstraint, because, becauseArgs);

return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that a string matches a regular expression.
/// </summary>
Expand Down Expand Up @@ -420,6 +430,21 @@ public AndConstraint<TAssertions> NotMatch(string wildcardPattern, string becaus
return MatchRegex(regex, because, becauseArgs);
}

public AndConstraint<TAssertions> MatchRegex(Regex regularExpression,
OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs)
{
int actual = regularExpression.Matches(Subject).Count;

Execute.Assertion
.ForConstraint(occurrenceConstraint, actual)
.UsingLineBreaks
.BecauseOf(because, becauseArgs)
.FailWith($"Expected {{context:string}} to match regex {{0}} {{expectedOccurrence}}{{reason}}, but found it {actual.Times()}.",
regularExpression.ToString());

return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that a string matches a regular expression.
/// </summary>
Expand All @@ -433,7 +458,8 @@ public AndConstraint<TAssertions> NotMatch(string wildcardPattern, string becaus
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public AndConstraint<TAssertions> MatchRegex(Regex regularExpression, string because = "", params object[] becauseArgs)
public AndConstraint<TAssertions> MatchRegex(Regex regularExpression,
string because = "", params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(regularExpression, nameof(regularExpression),
"Cannot match string against <null>. Provide a regex pattern or use the BeNull method.");
Expand Down
Expand Up @@ -2121,6 +2121,8 @@ namespace FluentAssertions.Primitives
public FluentAssertions.AndConstraint<TAssertions> MatchEquivalentOf(string wildcardPattern, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(string regularExpression, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(System.Text.RegularExpressions.Regex regularExpression, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(string regularExpression, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(System.Text.RegularExpressions.Regex regularExpression, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBe(string unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEmpty(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEquivalentTo(string unexpected, string because = "", params object[] becauseArgs) { }
Expand Down
120 changes: 2 additions & 118 deletions Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt

Large diffs are not rendered by default.

Expand Up @@ -2121,6 +2121,8 @@ namespace FluentAssertions.Primitives
public FluentAssertions.AndConstraint<TAssertions> MatchEquivalentOf(string wildcardPattern, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(string regularExpression, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(System.Text.RegularExpressions.Regex regularExpression, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(string regularExpression, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(System.Text.RegularExpressions.Regex regularExpression, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBe(string unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEmpty(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEquivalentTo(string unexpected, string because = "", params object[] becauseArgs) { }
Expand Down
Expand Up @@ -2121,6 +2121,8 @@ namespace FluentAssertions.Primitives
public FluentAssertions.AndConstraint<TAssertions> MatchEquivalentOf(string wildcardPattern, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(string regularExpression, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(System.Text.RegularExpressions.Regex regularExpression, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(string regularExpression, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(System.Text.RegularExpressions.Regex regularExpression, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBe(string unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEmpty(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEquivalentTo(string unexpected, string because = "", params object[] becauseArgs) { }
Expand Down
Expand Up @@ -2073,6 +2073,8 @@ namespace FluentAssertions.Primitives
public FluentAssertions.AndConstraint<TAssertions> MatchEquivalentOf(string wildcardPattern, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(string regularExpression, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(System.Text.RegularExpressions.Regex regularExpression, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(string regularExpression, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(System.Text.RegularExpressions.Regex regularExpression, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBe(string unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEmpty(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEquivalentTo(string unexpected, string because = "", params object[] becauseArgs) { }
Expand Down
Expand Up @@ -2121,6 +2121,8 @@ namespace FluentAssertions.Primitives
public FluentAssertions.AndConstraint<TAssertions> MatchEquivalentOf(string wildcardPattern, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(string regularExpression, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(System.Text.RegularExpressions.Regex regularExpression, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(string regularExpression, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> MatchRegex(System.Text.RegularExpressions.Regex regularExpression, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBe(string unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEmpty(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEquivalentTo(string unexpected, string because = "", params object[] becauseArgs) { }
Expand Down
Expand Up @@ -199,6 +199,35 @@ public void When_a_string_is_matched_against_an_empty_regex_it_should_throw_with
.WithParameterName("regularExpression");
}

[Fact]
public void When_a_string_is_matched_against_a_regex_and_the_count_of_matches_are_exactly_the_expected_it_passes()
{
// Arrange
string subject = "hello world";

// Act
Action act = () => subject.Should().MatchRegex(new Regex("hello.*"), Exactly.Once());

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

[Fact]
public void When_a_string_is_matched_against_a_regex_and_the_count_of_matches_are_not_the_expected_it_fails()
{
// Arrange
string subject = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " +
"ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et " +
"ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.";

// Act
Action act = () => subject.Should().MatchRegex("Lorem.*", Exactly.Twice());

// Assert
act.Should().Throw<XunitException>()
.WithMessage($"Expected subject to match regex*\"Lorem.*\" exactly 2 times, but found it 1 time.");
}

#endregion

#region Not Match Regex
Expand Down
2 changes: 2 additions & 0 deletions docs/_pages/releases.md
Expand Up @@ -14,6 +14,8 @@ sidebar:
* Added support for .NET6 `DateOnly` struct - [#1844](https://github.com/fluentassertions/fluentassertions/pull/1844)
* Added support for .NET6 `TimeOnly` struct - [#1848](https://github.com/fluentassertions/fluentassertions/pull/1848)
* Added `NotBe` for nullable boolean values - [#1865](https://github.com/fluentassertions/fluentassertions/pull/1865)
* Added ability to count regex matches - [#1869](https://github.com/fluentassertions/fluentassertions/pull/1869)

IT-VBFK marked this conversation as resolved.
Show resolved Hide resolved

### Fixes
* `EnumAssertions.Be` did not determine the caller name - [#1835](https://github.com/fluentassertions/fluentassertions/pull/1835)
Expand Down
7 changes: 7 additions & 0 deletions docs/_pages/strings.md
Expand Up @@ -108,3 +108,10 @@ someString.Should().MatchRegex(new System.Text.RegularExpressions.Regex("h.*\\sw
subject.Should().NotMatchRegex(new System.Text.RegularExpressions.Regex(".*earth.*"));
subject.Should().NotMatchRegex(".*earth.*");
```

And if that's not enough, you can count the occurrence of a regular expression:
IT-VBFK marked this conversation as resolved.
Show resolved Hide resolved

```csharp
someString.Should().MatchRegex("h.*\\sworld.$", Exactly.Once());
someString.Should().MatchRegex(new System.Text.RegularExpressions.Regex("h.*\\sworld.$"), AtLeast.Twice());
IT-VBFK marked this conversation as resolved.
Show resolved Hide resolved
```