Question: Same/similar fluent interface for in process and admin client API #123

Closed
opened 2025-12-29 14:23:09 +01:00 by adam · 11 comments
Owner

Originally created by @seanamosw on GitHub (Aug 3, 2018).

The API for stubbing and finding requests in process is done with a fluent API while using the admin client uses object construction. This can make it challenging to work with WireMock in containers or out of process.

In process:

var server = FluentMockServer.Start();

var id = Guid.NewGuid();

var request = 
    Request.Create()
        .UsingPut()
        .WithPath("/test")
        .WithBody($"*{id}*");
        
server.Given(request)
    .RespondWith(
        Response.Create()
            .WithStatusCode(200)
            .WithBody("Test!!"));
...

var logs = server.FindLogEntries(request);

The same with admin interface:

var admin = RestEase.RestClient.For<IFluentMockServerAdmin>("http://mock-container");

var id = Guid.NewGuid();

var requestModel = new RequestModel
{
    Methods = new [] { "put" },
    Path = "/test",
    Body = new BodyModel { Matcher = new MatcherModel
    {
        Name = "WildcardMatcher",
        Pattern = $"*{id}*"
    }}
};

await admin.PostMappingAsync(new MappingModel
{
    Request = requestModel,
    Response = new ResponseModel
    {
        StatusCode = 200,
        Body = "Test!!"
    }
});

...

var logs = await admin.FindRequestsAsync(requestModel);

The more complex the request/response model the more verbose it becomes.
The difference I think stems from the builders not creating a serializable model.

Possible solutions/improvements

1. Request/Response model builders are changed to produce serializable models.

There is already code to "interpret" Request/Response/Mapping models.

Example (proposed API is not important, the idea is):

var requestModel = RequestModelBuilder.xxx(); // builder that creates RequestModel
var responseModel = ResponseModelBuilder.xxx(); // builder that create ResponseModel

server.Given(requestModel)
      .RespondsWith(responseModel); // creates and interprets MappingModel

admin.PostMappingAsync(
    MappingModel.Given(requestModel)
                .RespondsWith(responseModel));
Pros
  • Can fluently construct serializable and wire friendly mappings
  • Unified API for both in process and remote
Cons
  • Breaking change
  • Would probably lose the ability to use lambdas in some builder methods like WithBody

2. Two fluent interfaces, add Given overload to FluentMockServer

The idea is that the current fluent interface is left intact and a second fluent interface is introduced that can create Request/Response/Mapping models.

Pros
  • No breaking change
  • Retain the ability to use in process lambdas
Cons
  • Two fluent interfaces to maintain
Originally created by @seanamosw on GitHub (Aug 3, 2018). The API for stubbing and finding requests in process is done with a fluent API while using the admin client uses object construction. This can make it challenging to work with WireMock in containers or out of process. In process: ```csharp var server = FluentMockServer.Start(); var id = Guid.NewGuid(); var request = Request.Create() .UsingPut() .WithPath("/test") .WithBody($"*{id}*"); server.Given(request) .RespondWith( Response.Create() .WithStatusCode(200) .WithBody("Test!!")); ... var logs = server.FindLogEntries(request); ``` The same with admin interface: ```csharp var admin = RestEase.RestClient.For<IFluentMockServerAdmin>("http://mock-container"); var id = Guid.NewGuid(); var requestModel = new RequestModel { Methods = new [] { "put" }, Path = "/test", Body = new BodyModel { Matcher = new MatcherModel { Name = "WildcardMatcher", Pattern = $"*{id}*" }} }; await admin.PostMappingAsync(new MappingModel { Request = requestModel, Response = new ResponseModel { StatusCode = 200, Body = "Test!!" } }); ... var logs = await admin.FindRequestsAsync(requestModel); ``` The more complex the request/response model the more verbose it becomes. The difference I think stems from the builders not creating a serializable model. ### Possible solutions/improvements #### 1. Request/Response model builders are changed to produce serializable models. There is already code to "interpret" Request/Response/Mapping models. Example (proposed API is not important, the idea is): ```csharp var requestModel = RequestModelBuilder.xxx(); // builder that creates RequestModel var responseModel = ResponseModelBuilder.xxx(); // builder that create ResponseModel server.Given(requestModel) .RespondsWith(responseModel); // creates and interprets MappingModel admin.PostMappingAsync( MappingModel.Given(requestModel) .RespondsWith(responseModel)); ``` ##### Pros - Can fluently construct serializable and wire friendly mappings - Unified API for both in process and remote ##### Cons - Breaking change - Would probably lose the ability to use lambdas in some builder methods like `WithBody` #### 2. Two fluent interfaces, add `Given` overload to `FluentMockServer` The idea is that the current fluent interface is left intact and a second fluent interface is introduced that can create Request/Response/Mapping models. ##### Pros - No breaking change - Retain the ability to use in process lambdas ##### Cons - Two fluent interfaces to maintain
adam closed this issue 2025-12-29 14:23:09 +01:00
Author
Owner

@StefH commented on GitHub (Aug 4, 2018):

The fluent interface is mostly designed to be:

  1. used in unit-testing
  2. used for some complex mocking functionality in a standalone which cannot be achieved by just running a server and providing the mappings via the admin REST interface.

The admin REST interface can be used from any tool (postman, soapui, ...) or programming lamguage (C#, java, javascript, ...).
The current C# client interface (build with RestEase) is just a very convenient interface for developers who what to access the admin REST interface using c# & dotnet.

So option 1 would not be my preference.

And about option 2, I wonder how the fluent interface should look?

Do you mean something like:

var requestModel = new RequestModel
{
   // manually define all properties here in code
};

var responseModel = new ResponseModel
{
   // manually define all properties here in code
};

var server = FluentMockServer.Start();

server.Given(requestModel).RespondWith(responseModel);
@StefH commented on GitHub (Aug 4, 2018): The fluent interface is mostly designed to be: 1. used in unit-testing 2. used for some complex mocking functionality in a standalone which cannot be achieved by just running a server and providing the mappings via the admin REST interface. The admin REST interface can be used from any tool (postman, soapui, ...) or programming lamguage (C#, java, javascript, ...). The current C# client interface (build with RestEase) is just a very convenient interface for developers who what to access the admin REST interface using c# & dotnet. So option 1 would _not_ be my preference. And about **option 2,** I wonder how the fluent interface should look? Do you mean something like: ``` c# var requestModel = new RequestModel { // manually define all properties here in code }; var responseModel = new ResponseModel { // manually define all properties here in code }; var server = FluentMockServer.Start(); server.Given(requestModel).RespondWith(responseModel); ```
Author
Owner

@seanamosw commented on GitHub (Aug 4, 2018):

To clarify, this idea is sort of a middle ground with what the Java Wiremock allows. You can point the java client API at a remote Wiremock process and leverage the fluent interface against it. In their case, the API was designed to treat Wiremock as always out of process, even if it is in process.

My immediate use case is using Wiremock.Net in C# integration tests running in containers. This means Wiremock is always out of process. Typically some stub setup needs to be done at the beginning of each test and possibly verifying outbound calls were made. At the moment this needs to be done by constructing the Model objects.

It works but being able to use a fluent interface to construct the Request/Response model would be a significant quality of life improvement.

@seanamosw commented on GitHub (Aug 4, 2018): To clarify, this idea is sort of a middle ground with what the Java Wiremock allows. You can point the java client API at a remote Wiremock process and leverage the fluent interface against it. In their case, the API was designed to treat Wiremock as always out of process, even if it is in process. My immediate use case is using Wiremock.Net in C# integration tests running in containers. This means Wiremock is always out of process. Typically some stub setup needs to be done at the beginning of each test and possibly verifying outbound calls were made. At the moment this needs to be done by constructing the Model objects. It works but being able to use a fluent interface to construct the Request/Response model would be a significant quality of life improvement.
Author
Owner

@seanamosw commented on GitHub (Aug 4, 2018):

In an ideal world, something similar to this would possible:

var requestModel =
    Request.Create()
        .UsingGet()
        .WithUrl("/test");

var responseModel =
    Response.Create()
        .WithStatusCode(200);

await admin.PostMappingAsync(new MappingModel
{
    Request = requestModel,
    Response = responseModel
});

// being able to use the same fluent interface between remote and in process
server.Given(requestModel).RespondWith(responseModel);
@seanamosw commented on GitHub (Aug 4, 2018): In an ideal world, something similar to this would possible: ```csharp var requestModel = Request.Create() .UsingGet() .WithUrl("/test"); var responseModel = Response.Create() .WithStatusCode(200); await admin.PostMappingAsync(new MappingModel { Request = requestModel, Response = responseModel }); // being able to use the same fluent interface between remote and in process server.Given(requestModel).RespondWith(responseModel); ```
Author
Owner

@StefH commented on GitHub (Aug 5, 2018):

a]
The new interface builder would have to look like:

var requestModel =
    RequestModelBuilder.Create()
        .UsingGet()
        .WithUrl("/test")
        .Build();

This means that the whole code from RequestBuilders needs to be copied.

I was hoping that I could use some autogenerating for that: https://github.com/ScottLilly/FluentInterfaceCreator, but this tool is not ready yet.

So for this reason, I'll not implement this new interface builder, it's too much work and I need to keep 2 interfaces in sync.

b]
Updating the current fluent interface to understand

server.Given(requestModel).RespondWith(responseModel);

can be done I think, not that much changes needed.

Maybe you can form my project, start a new branch and make a PR for this?

@StefH commented on GitHub (Aug 5, 2018): **a]** The new interface builder would have to look like: ``` c# var requestModel = RequestModelBuilder.Create() .UsingGet() .WithUrl("/test") .Build(); ``` This means that the whole code from [RequestBuilders](https://github.com/WireMock-Net/WireMock.Net/tree/master/src/WireMock.Net/RequestBuilders) needs to be copied. I was hoping that I could use some autogenerating for that: https://github.com/ScottLilly/FluentInterfaceCreator, but this tool is not ready yet. So for this reason, I'll not implement this new interface builder, it's too much work and I need to keep 2 interfaces in sync. **b]** Updating the current fluent interface to understand ``` c# server.Given(requestModel).RespondWith(responseModel); ``` can be done I think, not that much changes needed. Maybe you can form my project, start a new branch and make a PR for this?
Author
Owner

@seanamosw commented on GitHub (Aug 5, 2018):

a. I agree, maintaining two fluent interfaces and keeping them in sync is a burden.
Without a breaking change, two fluent interfaces to achieve this would be unavoidable.

Another option that could work is adding a separate lib, eg. WireMock.Net.ModelBuilders That could have the fluent interface for building the request/response model builders without polluting the main library with two fluent interfaces. I'd be happy to implement and maintain that.

b. I can do that.

@seanamosw commented on GitHub (Aug 5, 2018): a. I agree, maintaining two fluent interfaces and keeping them in sync is a burden. Without a breaking change, two fluent interfaces to achieve this would be unavoidable. Another option that could work is adding a separate lib, eg. `WireMock.Net.ModelBuilders` That could have the fluent interface for building the request/response model builders without polluting the main library with two fluent interfaces. I'd be happy to implement and maintain that. b. I can do that.
Author
Owner

@StefH commented on GitHub (Aug 6, 2018):

a] Maybe it's a good idea that I create this project under the https://github.com/WireMock-Net organization and give you (and me) access to that.

Also the NuGet package should be registered to both you and me if possible.

b] OK

@StefH commented on GitHub (Aug 6, 2018): a] Maybe it's a good idea that I create this project under the https://github.com/WireMock-Net organization and give you (and me) access to that. Also the NuGet package should be registered to both you and me if possible. b] OK
Author
Owner

@StefH commented on GitHub (Aug 6, 2018):

a] I did send invite to you, if you accept, I can give you access to this project.
The license is apache 2.0 ; but we can also change this into MIT? I don't know the real difference and what's better.

@StefH commented on GitHub (Aug 6, 2018): a] I did send invite to you, if you accept, I can give you access to this project. The license is apache 2.0 ; but we can also change this into MIT? I don't know the real difference and what's better.
Author
Owner

@seanamosw commented on GitHub (Aug 7, 2018):

Thanks.
MIT is often a go to license, simple and permissive. It doesn't have the "State Changes" clause of Apache 2.0.

@seanamosw commented on GitHub (Aug 7, 2018): Thanks. MIT is often a go to license, simple and permissive. It doesn't have the "State Changes" clause of Apache 2.0.
Author
Owner

@StefH commented on GitHub (Aug 7, 2018):

I've also added you as contributor to that new project.

Can this issue be closed?

(New issues or ideas can also be added to that project.)

@StefH commented on GitHub (Aug 7, 2018): I've also added you as contributor to that new project. Can this issue be closed? (New issues or ideas can also be added to that project.)
Author
Owner

@seanamosw commented on GitHub (Aug 7, 2018):

Yes, I think we have a way forward so any further discussion can be moved there.

@seanamosw commented on GitHub (Aug 7, 2018): Yes, I think we have a way forward so any further discussion can be moved there.
Author
Owner

@StefH commented on GitHub (Aug 9, 2020):

@seanamosw
Did you continue on the code in the develop-branch from the https://github.com/WireMock-Net/WireMock.Net.ModelBuilders/tree/develop/ project?

@StefH commented on GitHub (Aug 9, 2020): @seanamosw Did you continue on the code in the `develop`-branch from the https://github.com/WireMock-Net/WireMock.Net.ModelBuilders/tree/develop/ project?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/WireMock.Net-wiremock#123