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
17 changes: 12 additions & 5 deletions Src/FluentAssertions/Primitives/StringAssertions.cs
Expand Up @@ -394,7 +394,10 @@ public AndConstraint<TAssertions> NotMatch(string wildcardPattern, string becaus
/// The regular expression with which the subject is matched.
/// </param>
/// <param name="occurrenceConstraint">
/// The excpected occurences of regex matches
/// A constraint specifying the expected amount of times a regex should match a string.
/// It can be created by invoking static methods Once, Twice, Thrice, or Times(int)
/// on the classes <see cref="Exactly"/>, <see cref="AtLeast"/>, <see cref="MoreThan"/>, <see cref="AtMost"/>, and <see cref="LessThan"/>.
/// For example, <see cref="Exactly.Times(int)"/> or <see cref="LessThan.Twice()"/>.
IT-VBFK marked this conversation as resolved.
Show resolved Hide resolved
/// </param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
Expand All @@ -406,7 +409,8 @@ public AndConstraint<TAssertions> NotMatch(string wildcardPattern, string becaus
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
Guard.ThrowIfArgumentIsNull(regularExpression, nameof(regularExpression), "Cannot match string against <null>. Provide a regex pattern or use the BeNull method.");
Guard.ThrowIfArgumentIsNull(regularExpression, nameof(regularExpression),
"Cannot match string against <null>. Provide a regex pattern or use the BeNull method.");

Regex regex;
try
Expand Down Expand Up @@ -463,7 +467,10 @@ public AndConstraint<TAssertions> NotMatch(string wildcardPattern, string becaus
/// The regular expression with which the subject is matched.
/// </param>
/// <param name="occurrenceConstraint">
/// The excpected occurences of regex matches
/// A constraint specifying the expected amount of times a regex should match a string.
/// It can be created by invoking static methods Once, Twice, Thrice, or Times(int)
/// on the classes <see cref="Exactly"/>, <see cref="AtLeast"/>, <see cref="MoreThan"/>, <see cref="AtMost"/>, and <see cref="LessThan"/>.
/// For example, <see cref="Exactly.Times(int)"/> or <see cref="LessThan.Twice()"/>.
/// </param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
Expand All @@ -476,7 +483,7 @@ public AndConstraint<TAssertions> NotMatch(string wildcardPattern, string becaus
OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(regularExpression, nameof(regularExpression),
"Cannot match string against <null>. Provide a regex pattern or use the BeNull method.");
"Cannot match string against <null>. Provide a regex pattern or use the BeNull method.");

var regexStr = regularExpression.ToString();
if (regexStr.Length == 0)
dennisdoomen marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -501,7 +508,7 @@ public AndConstraint<TAssertions> NotMatch(string wildcardPattern, string becaus
.UsingLineBreaks
.BecauseOf(because, becauseArgs)
.FailWith($"Expected {{context:string}} to match regex {{0}} {{expectedOccurrence}}{{reason}}, but found it {actual.Times()}.",
regularExpression.ToString());
regexStr);
}

return new AndConstraint<TAssertions>((TAssertions)this);
Expand Down
Expand Up @@ -200,20 +200,20 @@ public void When_a_string_is_matched_against_an_empty_regex_it_should_throw_with
}

[Fact]
public void When_a_string_is_matched_against_a_regex_and_the_count_of_matches_are_exactly_the_expected_it_passes()
public void When_a_string_is_matched_against_a_regex_and_the_count_of_matches_fits_into_the_expected_it_passes()
dennisdoomen marked this conversation as resolved.
Show resolved Hide resolved
{
// Arrange
string subject = "hello world";

// Act
Action act = () => subject.Should().MatchRegex(new Regex("hello.*"), Exactly.Once());
Action act = () => subject.Should().MatchRegex(new Regex("hello.*"), AtLeast.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()
public void When_a_string_is_matched_against_a_regex_and_the_count_of_matches_do_not_fit_the_expected_it_fails()
{
// Arrange
string subject = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " +
Expand Down Expand Up @@ -255,6 +255,103 @@ public void When_a_string_is_matched_against_a_regex_and_the_expected_count_is_z
.WithMessage($"Expected subject to match regex*\"a\" exactly 0 times, but found it 1 time.");
}

[Fact]
public void When_the_subject_is_null_it_fails()
{
// Arrange
string subject = null;

// Act
Action act = () =>
{
using var _ = new AssertionScope();
subject.Should().MatchRegex(".*", Exactly.Times(0), "because it should be a string");
};

// Assert
act.Should().ThrowExactly<XunitException>()
.WithMessage("Expected subject to match regex*\".*\" because it should be a string, but it was <null>.");
}

[Fact]
public void When_the_subject_is_empty_and_expected_count_is_zero_and_the_regex_matches_it_passes()
{
// Arrange
string subject = string.Empty;

// Act
Action act = () =>
{
using var _ = new AssertionScope();
subject.Should().MatchRegex(".+", Exactly.Times(0));
};

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

[Fact]
public void When_the_subject_is_empty_and_expected_count_is_more_than_zero_it_fails()
{
// Arrange
string subject = string.Empty;

// Act
Action act = () =>
{
using var _ = new AssertionScope();
subject.Should().MatchRegex(".+", AtLeast.Once());
};

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

[Fact]
public void When_regex_is_null_it_fails_and_ignores_occurrences()
{
// Arrange
string subject = "a";

// Act
Action act = () => subject.Should().MatchRegex((Regex)null, Exactly.Times(0));

// Assert
act.Should().ThrowExactly<ArgumentNullException>()
.WithMessage("Cannot match string against <null>. Provide a regex pattern or use the BeNull method.*")
.WithParameterName("regularExpression");
}

[Fact]
public void When_regex_is_empty_it_fails_and_ignores_occurrences()
{
// Arrange
string subject = "a";

// Act
Action act = () => subject.Should().MatchRegex(string.Empty, Exactly.Times(0));

// Assert
act.Should().ThrowExactly<ArgumentException>()
.WithMessage("Cannot match string against an empty string. Provide a regex pattern or use the BeEmpty method.*")
.WithParameterName("regularExpression");
}

[Fact]
public void When_regex_is_invalid_it_fails_and_ignores_occurrences()
{
// Arrange
string subject = "a";

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

// Assert
act.Should().ThrowExactly<XunitException>()
.WithMessage("Cannot match subject against \".**\" because it is not a valid regular expression.*");
}

#endregion

#region Not Match Regex
Expand Down