Logging failed mappings #393

Closed
opened 2025-12-29 15:22:37 +01:00 by adam · 7 comments
Owner

Originally created by @LevYas on GitHub (Dec 20, 2021).

I'm developing an app that works with several other APIs, and the typical scenario is to record some interactions and use these records later as test data. Sometimes I need to edit a third-party response to a specific request, and I ofter run into trouble "no specific matching found". I thought it would be ideal to save the data about failed matching to analyze it. How can I do it?

I use wiremock.net in tests like described here: https://github.com/WireMock-Net/WireMock.Net/wiki/Using-WireMock-in-UnitTests
so my option is to use the last method from these tips (to access log entries via C# code): https://github.com/WireMock-Net/WireMock.Net/wiki/Request-Matching-Tips

I use it like this:

        private readonly WireMockServer _authServer = create(...);

        private static WireMockServer create(...)
        {
            var settings = new WireMockServerSettings
            {
                Urls = new[] { mockUri.AbsoluteUri },
                FileSystemHandler = new MyLocalFileSystemHandler(...),
                ReadStaticMappings = true,
            };
            return WireMockServer.Start(settings);
        }

        public void Stop()
        {
            logFailedRequests(_authServer);
            _authServer.Stop();
        }

        private static void logFailedRequests(WireMockServer server)
        {
            foreach (ILogEntry entry in server.LogEntries.Where(e => !e.PartialMatchResult.IsPerfectMatch))
            {
                string ser = JsonConvert.SerializeObject(entry, Formatting.Indented);
                File.WriteAllText(Path.Combine(..., entry.Guid + ".json"), ser);
            }
        }

The problem is that I have several mock servers, and even if I move logFailedRequests to some utility class, it's a bit clunky to repeat this call in every mock before stopping and disposing. Right now, the only thing I can think of is, is to build a facade around WireMockNet and do the logging there.

What can I do to simplify this? Maybe some configuration trick or overriding of something.

Originally created by @LevYas on GitHub (Dec 20, 2021). I'm developing an app that works with several other APIs, and the typical scenario is to record some interactions and use these records later as test data. Sometimes I need to edit a third-party response to a specific request, and I ofter run into trouble "no specific matching found". I thought it would be ideal to save the data about failed matching to analyze it. How can I do it? I use wiremock.net in tests like described here: https://github.com/WireMock-Net/WireMock.Net/wiki/Using-WireMock-in-UnitTests so my option is to use the last method from these tips (to access log entries via C# code): https://github.com/WireMock-Net/WireMock.Net/wiki/Request-Matching-Tips I use it like this: ``` c# private readonly WireMockServer _authServer = create(...); private static WireMockServer create(...) { var settings = new WireMockServerSettings { Urls = new[] { mockUri.AbsoluteUri }, FileSystemHandler = new MyLocalFileSystemHandler(...), ReadStaticMappings = true, }; return WireMockServer.Start(settings); } public void Stop() { logFailedRequests(_authServer); _authServer.Stop(); } private static void logFailedRequests(WireMockServer server) { foreach (ILogEntry entry in server.LogEntries.Where(e => !e.PartialMatchResult.IsPerfectMatch)) { string ser = JsonConvert.SerializeObject(entry, Formatting.Indented); File.WriteAllText(Path.Combine(..., entry.Guid + ".json"), ser); } } ``` The problem is that I have several mock servers, and even if I move `logFailedRequests` to some utility class, it's a bit clunky to repeat this call in every mock before stopping and disposing. Right now, the only thing I can think of is, is to build a facade around WireMockNet and do the logging there. What can I do to simplify this? Maybe some configuration trick or overriding of something.
adam added the question label 2025-12-29 15:22:37 +01:00
adam closed this issue 2025-12-29 15:22:37 +01:00
Author
Owner

@StefH commented on GitHub (Dec 20, 2021):

Hello @LevYas,

Can you try version: 1.4.29-ci-15687?

I did add a new config setting : SaveUnmatchedRequests which should be set to true,

https://github.com/WireMock-Net/WireMock.Net/pull/703

@StefH commented on GitHub (Dec 20, 2021): Hello @LevYas, Can you try version: `1.4.29-ci-15687`? I did add a new config setting : `SaveUnmatchedRequests` which should be set to true, https://github.com/WireMock-Net/WireMock.Net/pull/703
Author
Owner

@LevYas commented on GitHub (Dec 21, 2021):

Wow, that was fast, thank you!

I love the API! I was able to set the SaveUnmatchedRequests to true and to override WriteUnmatchedRequest and it logged the failed requests.

But I found it very useful to have all the information about mapping and the closest match, like in the LogEntry, so I can see the closest match and what check(s) failed - headers or body or maybe URL. Can the same data be used for saving unmatched requests?

Also, what is the intended way to customize LocalFileSystemHandler? I wanted to change only parts of it to change paths, but it's a bit difficult, and changing the path to UnmatchedRequests didn't work - it was never called and never used:

public class MyLocalFileSystemHandler : LocalFileSystemHandler, IFileSystemHandler
{
    private readonly string _mappingFolder;

    public MyLocalFileSystemHandler(string mappingFolder) => _mappingFolder = mappingFolder;

    string IFileSystemHandler.GetMappingFolder() => _mappingFolder;
    string IFileSystemHandler.GetUnmatchedRequestsFolder() => _mappingFolder;
}

Though overriding GetMappingFolder worked well, don't understand why the second didn't work.
Anyway, this worked:

public class MyLocalFileSystemHandler : LocalFileSystemHandler, IFileSystemHandler
{
    private readonly string _mappingFolder;

    public MyLocalFileSystemHandler(string mappingFolder) => _mappingFolder = mappingFolder;

    string IFileSystemHandler.GetMappingFolder() => _mappingFolder;

    void IFileSystemHandler.WriteUnmatchedRequest(string filename, string text)
    {
        File.WriteAllText(Path.Combine(_mappingFolder, filename), text);
    }
}

The overriding would be easier if the methods are virtual, but maybe I don't use the tool as intended and there are simpler ways to achieve that.

@LevYas commented on GitHub (Dec 21, 2021): Wow, that was fast, thank you! I love the API! I was able to set the `SaveUnmatchedRequests` to true and to override `WriteUnmatchedRequest` and it logged the failed requests. But I found it very useful to have all the information about mapping and the closest match, like in the `LogEntry`, so I can see the closest match and what check(s) failed - headers or body or maybe URL. Can the same data be used for saving unmatched requests? Also, what is the intended way to customize LocalFileSystemHandler? I wanted to change only parts of it to change paths, but it's a bit difficult, and changing the path to UnmatchedRequests didn't work - it was never called and never used: public class MyLocalFileSystemHandler : LocalFileSystemHandler, IFileSystemHandler { private readonly string _mappingFolder; public MyLocalFileSystemHandler(string mappingFolder) => _mappingFolder = mappingFolder; string IFileSystemHandler.GetMappingFolder() => _mappingFolder; string IFileSystemHandler.GetUnmatchedRequestsFolder() => _mappingFolder; } Though overriding GetMappingFolder worked well, don't understand why the second didn't work. Anyway, this worked: public class MyLocalFileSystemHandler : LocalFileSystemHandler, IFileSystemHandler { private readonly string _mappingFolder; public MyLocalFileSystemHandler(string mappingFolder) => _mappingFolder = mappingFolder; string IFileSystemHandler.GetMappingFolder() => _mappingFolder; void IFileSystemHandler.WriteUnmatchedRequest(string filename, string text) { File.WriteAllText(Path.Combine(_mappingFolder, filename), text); } } The overriding would be easier if the methods are virtual, but maybe I don't use the tool as intended and there are simpler ways to achieve that.
Author
Owner

@StefH commented on GitHub (Dec 21, 2021):

Can you try 1.4.29-ci-15699?

About your question about the folder, I don't know. Can you take a look at the source code? Maybe I made a mistake?
https://github.com/WireMock-Net/WireMock.Net/blob/stef-702-log-failed-requests/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs

And indeed, currently this class has no virtual methods yet, I'll think about this...
#703

@StefH commented on GitHub (Dec 21, 2021): Can you try `1.4.29-ci-15699`? About your question about the folder, I don't know. Can you take a look at the source code? Maybe I made a mistake? https://github.com/WireMock-Net/WireMock.Net/blob/stef-702-log-failed-requests/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs And indeed, currently this class has no virtual methods yet, I'll think about this... #703
Author
Owner

@LevYas commented on GitHub (Dec 22, 2021):

Thank you!

The logged file contains the data I wanted, cool, thanks!

About your question about the folder, I don't know.

I think I found the reason: For writing the mappings in Proxy you use WriteMappingFile and you provide the full path as an argument. The proxy uses the interface, so it finds the interface implementation in my class.
On the other hand, for unmatched requests WriteUnmatchedRequest performs a "local" call to GetUnmatchedRequestsFolder(), so all the calls stay within the class. So the only way to override this behavior is to reimplement WriteUnmatchedRequest.
That is fine, but if the path adjustment is easier, it'd be superb.

Just for the context, my use-case is to record some data to TestData, maybe remove content-length matching and adjust a couple of fields in the body. I commit the TestData to Git and use these records in my local and CI tests. So LocalFileSystemHandler is perfectly fine in general, I just tune it to save and load records to and from the TestData folder.
When something goes wrong, I get the unmatched request in the same TestData folder, so it appears as an uncommitted file on the Git panel in VisualStudio and I can immediately open it and see what was wrong, fix the problem, and delete the file. In that case, it works like a snapshot test for my requests with debugging aids.

From the top of my head, I'd suggest either

  • specifying in LocalFileSystemHandler's constructor full paths to directories to save mappings and requests (RootFolder would not be enough, i.e. I don't need subfolders __admin or requests)
  • make GetUnmatchedRequestsFolder() and GetMappingFolder() virtual, but it'd be more clunky to use
@LevYas commented on GitHub (Dec 22, 2021): Thank you! The logged file contains the data I wanted, cool, thanks! > About your question about the folder, I don't know. I think I found the reason: For writing the mappings in Proxy you use `WriteMappingFile` and you provide the full path as an argument. The proxy uses the interface, so it finds the interface implementation in my class. On the other hand, for unmatched requests `WriteUnmatchedRequest` performs a "local" call to `GetUnmatchedRequestsFolder()`, so all the calls stay within the class. So the only way to override this behavior is to reimplement `WriteUnmatchedRequest`. That is fine, but if the path adjustment is easier, it'd be superb. Just for the context, my use-case is to record some data to TestData, maybe remove content-length matching and adjust a couple of fields in the body. I commit the TestData to Git and use these records in my local and CI tests. So LocalFileSystemHandler is perfectly fine in general, I just tune it to save and load records to and from the TestData folder. When something goes wrong, I get the unmatched request in the same TestData folder, so it appears as an uncommitted file on the Git panel in VisualStudio and I can immediately open it and see what was wrong, fix the problem, and delete the file. In that case, it works like a snapshot test for my requests with debugging aids. From the top of my head, I'd suggest either - specifying in `LocalFileSystemHandler`'s constructor full paths to directories to save mappings and requests (RootFolder would not be enough, i.e. I don't need subfolders __admin or requests) - make `GetUnmatchedRequestsFolder()` and `GetMappingFolder()` virtual, but it'd be more clunky to use
Author
Owner

@StefH commented on GitHub (Dec 23, 2021):

I've added virtual to all methods.

Now, If you just override the GetUnmatchedRequestsFolder, all should work fine?

New version = 1.4.29-ci-15701;

@StefH commented on GitHub (Dec 23, 2021): I've added virtual to all methods. Now, If you just override the `GetUnmatchedRequestsFolder`, all should work fine? New version = 1.4.29-ci-15701;
Author
Owner

@LevYas commented on GitHub (Dec 23, 2021):

Thank you!
Just checked, that worked, and the code was simplified significantly, well done! 👍

public class MyLocalFileSystemHandler : LocalFileSystemHandler
{
    private readonly string _mappingFolder;
    public MyLocalFileSystemHandler(string mappingFolder) => _mappingFolder = mappingFolder;

    public override string GetMappingFolder() => _mappingFolder;
    public override string GetUnmatchedRequestsFolder() => _mappingFolder;
}
@LevYas commented on GitHub (Dec 23, 2021): Thank you! Just checked, that worked, and the code was simplified significantly, well done! 👍 public class MyLocalFileSystemHandler : LocalFileSystemHandler { private readonly string _mappingFolder; public MyLocalFileSystemHandler(string mappingFolder) => _mappingFolder = mappingFolder; public override string GetMappingFolder() => _mappingFolder; public override string GetUnmatchedRequestsFolder() => _mappingFolder; }
Author
Owner

@StefH commented on GitHub (Dec 24, 2021):

OK. I'll merge this PR and create a new official version in some time.

@StefH commented on GitHub (Dec 24, 2021): OK. I'll merge this PR and create a new official version in some time.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/WireMock.Net-wiremock#393