Skip to content

Conversation

@skwasjer
Copy link
Owner

@skwasjer skwasjer commented Nov 28, 2025

This PR adds a new type Matches (see below) and adds a new overload RequestMatchingExtensions.RequestUri(RequestMatching, Matches).

Relates to #112

The behavior of the sibling overload accepting a string RequestMatchingExtensions.RequestUri(RequestMatching, string) has now changed. It now no longer infers a wildcard match if the input string contains an asterisk '*'. It now only expects relative or absolute URI's and will match them exactly as before.
Unfortunately, changing the behavior will cause friction amongst users that have used wildcards in their tests, but I see no other way to transition to this new API.

To perform wildcard matches, it must now be explicitly provided via the overload accepting a Matches type, eg. using Matches.Wildcard("some*thing"). By using this explicit approach, we give users full control over how a request URI can be matched, and remove the ambiguity that was present with the string/wildcard inference. With the new API users can even use custom expression, regexes, etc.

Matches struct

The new Matches struct encapsulates string matching, so there is one unified API that can be used internally my MockHttp to match request URI's but also in the future headers, and URI components separately (eg. query string, path, authority, etc.).

IMPORTANT: no URL (data) encoding/decoding is performed when using the Matches type, thus the user of MockHttp must make sure the value is encoded properly.

The Matches type provides several helpers:

  • Match using wildcard(s) *:
    Matches.Wildcard("*/controller/*/action?query=*")
  • Match using regex:
    Matches.Regex(".+/controller/\w+/action?query=.+")
    // or
    Matches.Regex(new Regex(".+/controller/.+/action?query=.+"))
  • Match using custom expression:
    Matches.Expression(s => s.Contains("/controller/") && s.Contains("/action?query="))

The following are less useful for request URI matching, but in the future the Matches type will also support matching query string and header key value pairs, which is where these will shine.

  • Match null or empty:
    Matches.Empty
  • Match anything:
    Matches.Any
  • Match exactly:
    Matches.Exactly("/controller/nested/action?query=1")

Operators

The Matches type has several operator implementations, which also allows mixing different pattern styles:

  • And
    Matches.Wildcard("*controller*") & Matches.Regex(".+/action?query=.+")
  • Or:
    Matches.Wildcard("*controller*") | Matches.Regex(".+/action?query=.+")
  • Xor:
    Matches.Wildcard("*controller*") ^ Matches.Regex(".+/action?query=.+")
  • Not:
    !Matches.Wildcard("*controller*")

Implicit conversion

string implicitly converts to Matches using Matches.Exactly(..)

ToString

When calling ToString(), returns the provided pattern value.

@skwasjer
Copy link
Owner Author

skwasjer commented Nov 29, 2025

Renaming Pattern to Matches would make more sense in the context of the Fluent API.
Similary, the Expression method should probably be named When.

You'd then get statements such as:

mockHttp
    .When(matching => matching
        .Method("GET")
        .RequestUri(Matches.When(uri => uri.StartsWith("http://")) & Matches.Wildcard("*/controller/action"))
    .Respond(with => with
        .StatusCode(200)
    );

It reads more natural this way.

Ideally, Pattern/Matches would also have a generic base interface or abstract base type, so we can use Uri in stead of string while at the same time have an API that could accept HeaderCollection for when this is implemented for HTTP headers.

The behavior of the sibling overload accepting a string has now changed. It no longer infers a wildcard match if the input string contains an asterisk '*'. It now only expects relative or absolute URI's and will match them exactly.

To perform wildcard matches, it must now be explicitly provided via the overload accepting a pattern, eg. using `Matches.Wildcard("some*thing")`. By using this explicit approach, we give users full control over how a request URI can be matched, and remove the ambiguity. With the new API users can even use custom expression, regexes, etc.

Unfortunately, changing the behavior will cause friction amongst users that have used wildcards in their tests, but I see no other way to transition to this new API.
@skwasjer skwasjer changed the title feat!: add new overload to match RequestUri, accepting Pattern feat!: add new overload to match RequestUri, accepting Matches Nov 29, 2025
@sonarqubecloud
Copy link

@skwasjer skwasjer marked this pull request as ready for review November 29, 2025 12:02
@skwasjer skwasjer added enhancement New feature or request .NET Pull requests that update .NET code labels Nov 29, 2025
@skwasjer skwasjer merged commit 6d02787 into main Dec 2, 2025
13 checks passed
@skwasjer skwasjer deleted the feature/patterns branch December 2, 2025 06:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request .NET Pull requests that update .NET code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants