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

[API Proposal]: Allow to assert RegEx matched groups #2625

Open
rklec opened this issue Apr 9, 2024 · 3 comments
Open

[API Proposal]: Allow to assert RegEx matched groups #2625

rklec opened this issue Apr 9, 2024 · 3 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation

Comments

@rklec
Copy link

rklec commented Apr 9, 2024

Background and motivation

Suppose I have the following rgeular expression (regex): (abc)(\d{3})([X-Z]*) (https://regex101.com/r/Aszkcm/1)

I can write it like this, and this is fine:

[Test]
public void SomethingReturnsValidCode()
{
    var resultOfSystemUnderTest = "abc123XY";

    resultOfSystemUnderTest.Should().MatchRegex(new Regex(@"(abc?)(\d{3})([X-Z]*)"));
}        

However, I have, AFAIK, no cool way to match the actual groups of the RegEx.

My motivation behind this here is validating the syntax of a code generator. It should, under some circumstances, e.g. return "123" as the second group in the RegEx. Note, as this assertion needs to target the middle of the string, it is very hard to match and assert without using RegEx (and arguably maybe as ugly, e.g. if as in my case the start and end could be different lengths...).
More complex use cases are possible, of course.

API Proposal

public class StringAssertions<TEnum, TAssertions>
{
    public AndConstraint<TAssertions> MatchRegex(Regex regularExpression,
        string because = "", params object[] becauseArgs)
}

Maybe MatchRegex just needs to be expanded to return an extended TAssertions that can handle the regex then?

API Usage

[Test]
public void SomethingReturnsValidCode()
{
    var resultOfSystemUnderTest = "abc123XY";

    resultOfSystemUnderTest.Should().MatchRegex(new Regex(@"(abc?)(\d{3})([X-Z]*)"))
        .And.Group[2].Should().Be("123")
        .And.Group[3].Should().Be("XY");
}

...or similar. (Indexers probably need to be used, as potentially many matches are possible).

Alternative Designs

  • maybe make RegexAssertions specifically? I dunno how exactly implementing it would be best.

Current solution would be (Chaining is also not really possible here):

resultOfSystemUnderTest.Match(result).Groups[2].Value.Should().Be("123"); // etc.

Risks

IMHO/AFAIK no breaking changes, performance should also be the same as the existing matches, because AFAIK the groups are anyway evaluated by C# if you match it, but I am not sure.

Are you willing to help with a proof-of-concept (as PR in that or a separate repo) first and as pull-request later on?

No

@rklec rklec added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Apr 9, 2024
@jnyrup jnyrup changed the title [API Proposal]: ALlow to assert RegEx matched groups [API Proposal]: Allow to assert RegEx matched groups Apr 10, 2024
@IT-VBFK
Copy link
Contributor

IT-VBFK commented Apr 10, 2024

What about this (to avoid this indexers):

[Test]
public void SomethingReturnsValidCode()
{
    var resultOfSystemUnderTest = "abc123XY";

    resultOfSystemUnderTest.Should().MatchRegex(new Regex(@"(abc?)(\d{3})([X-Z]*)"))
        .Which.Groups.Should().SatisfyRespectively(group0 => {}, groupX => { groupX.Should().Be("123") }, ...);
}

The only drawback is, that you have to add all inspectors (like group0), which are empty if you want to ignore them...

@jnyrup
Copy link
Member

jnyrup commented Apr 10, 2024

If the method signature MatchRegex is changed to AndWhichConstraint<TAssertions, Match> and #2597 gets merged, it could even be written as

[Test]
public void SomethingReturnsValidCode()
{
    var resultOfSystemUnderTest = "abc123XY";

    resultOfSystemUnderTest.Should().MatchRegex(new Regex(@"(abc?)(\d{3})([X-Z]*)"))
        .Which.Should().Satisfy<Match>(match =>
        {
            match.Groups[2].Should().Be("123");
            match.Groups[3].Should().Be("XY");
        });
}

One microscopic downside is that MatchRegex will need to switch from the lighter Regex.IsMatch to Regex.Match, but I don't think that will be noticeable from test.

@dennisdoomen
Copy link
Member

Or even more idiomatic FA like

var resultOfSystemUnderTest = "abc123XY";

resultOfSystemUnderTest.Should().MatchRegex(@"(abc?)(\d{3})([X-Z]*)")
    .WithGroup(2).Being("123")
    .And.WithGroup(3).Being("XY");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation
Projects
None yet
Development

No branches or pull requests

4 participants