mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-18 15:10:17 +02:00
12 KiB
12 KiB
WebSocket Fluent Interface - Quick Reference
At a Glance
Current Architecture (HTTP Only)
// Request matching
Request.Create()
.WithPath("/api")
.WithHeader("...")
.UsingGet()
// Response building
Response.Create()
.WithStatusCode(200)
.WithBodyAsJson(...)
.WithTransformer()
// Mapping
server.Given(request)
.AtPriority(1)
.InScenario("...")
.RespondWith(response)
Proposed Addition (WebSocket Support)
// WebSocket request matching
Request.Create()
.WithWebSocketPath("/ws")
.WithWebSocketSubprotocol("chat")
// WebSocket response building
Response.Create()
.WithWebSocket(ws => ws
.WithMessage("Hello")
.WithJsonMessage(obj)
.WithTransformer()
)
.WithWebSocketCallback(async req => ... messages ...)
// Mapping (same as HTTP)
server.Given(request)
.RespondWith(response)
Quick Comparison: HTTP vs WebSocket Fluent API
Request Builder
| HTTP | WebSocket |
|---|---|
WithPath(string) |
WithWebSocketPath(string) |
WithHeader(string, string) |
WithHeader(...) (same) |
UsingGet() |
WithWebSocketUpgrade() (implicit) |
WithParam(string, string) |
(not applicable) |
WithBody(string) |
(connection is upgrade, no body) |
Response Builder
| HTTP | WebSocket |
|---|---|
WithStatusCode(int) |
WithWebSocketClose(int) |
WithBody(string) |
WithMessage(string) |
WithBodyAsJson(object) |
WithJsonMessage(object) |
| (binary: rarely used) | WithBinaryMessage(byte[]) |
WithCallback(...) |
WithWebSocketCallback(...) |
WithTransformer() |
WithTransformer() (same) |
Mapping Configuration
| Feature | HTTP | WebSocket |
|---|---|---|
| Priority | ✓ AtPriority(int) |
✓ AtPriority(int) |
| Scenario | ✓ InScenario(...) |
✓ InScenario(...) |
| Webhook | ✓ WithWebhook(...) |
✓ WithWebhook(...) |
| Title/Desc | ✓ WithTitle(...) |
✓ WithTitle(...) |
Implementation Checklist
Phase 1: Abstractions
- Create
IWebSocketMessageinterface - Create
IWebSocketResponseinterface - Create
IWebSocketResponseBuilderinterface - Add
WebSocketModelto admin mappings - Extend
IRequestBuilderwith WebSocket methods - Extend
IResponseBuilderwith WebSocket methods
Phase 2: Domain Models
- Implement
WebSocketMessageclass - Implement
WebSocketResponseclass
Phase 3: Request Builder Extension
- Create
Request.WithWebSocket.cspartial class - Implement
WithWebSocketUpgrade() - Implement
WithWebSocketPath() - Implement
WithWebSocketSubprotocol() - Add unit tests
Phase 4: Response Builder Extension
- Create
Response.WithWebSocket.cspartial class - Implement WebSocket response methods
- Create
WebSocketResponseBuilder.cs - Add transformer support
- Add callback support
- Add unit tests
Phase 5: Server Integration
- Update
WireMockMiddleware.csto handle WebSocket upgrades - Implement WebSocket connection handling
- Implement message delivery
- Add connection lifecycle management
- Add integration tests
Phase 6: Admin Interface
- Extend
MappingModelwith WebSocket config - Update mapping serialization
- Add REST API endpoints for WebSocket management
File Changes Summary
New Files (Abstractions)
src/WireMock.Net.Abstractions/
├── Models/IWebSocketMessage.cs
├── Models/IWebSocketResponse.cs
├── BuilderExtensions/IWebSocketResponseBuilder.cs
└── Admin/Mappings/WebSocketModel.cs
New Files (Implementation)
src/WireMock.Net.Minimal/
├── Models/WebSocketMessage.cs
├── Models/WebSocketResponse.cs
├── RequestBuilders/Request.WithWebSocket.cs
├── ResponseBuilders/Response.WithWebSocket.cs
└── ResponseBuilders/WebSocketResponseBuilder.cs
Modified Files
src/WireMock.Net.Minimal/
├── ResponseBuilders/Response.cs (add interface definitions)
├── RequestBuilders/Request.cs (add interface definitions)
├── Server/WireMockServer.cs (WebSocket support)
├── Owin/WireMockMiddleware.cs (handle upgrades)
└── Owin/MappingMatcher.cs (WebSocket routing)
Code Examples
Simple WebSocket
// Echo server
server.Given(Request.Create().WithWebSocketPath("/echo"))
.RespondWith(Response.Create()
.WithWebSocketCallback(async req =>
new[] { new WebSocketMessage { BodyAsString = req.Body } }
)
);
Messages with Delays
// Multi-message response
server.Given(Request.Create().WithWebSocketPath("/stream"))
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithMessage("First", 0)
.WithMessage("Second", 500)
.WithMessage("Third", 1000)
)
);
Dynamic Messages
// Request-based generation
server.Given(Request.Create().WithWebSocketPath("/api"))
.RespondWith(Response.Create()
.WithWebSocketCallback(async request =>
{
var userId = request.Headers["X-User-Id"].First();
return new[]
{
new WebSocketMessage
{
BodyAsString = $"Hello {userId}"
}
};
})
);
Templated Messages
// Handlebars in message content
server.Given(Request.Create().WithWebSocketPath("/api"))
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithJsonMessage(new
{
user = "{{request.headers.X-User}}"
})
.WithTransformer()
)
);
Subprotocol Negotiation
// Version-specific behavior
server.Given(Request.Create()
.WithWebSocketPath("/api")
.WithWebSocketSubprotocol("v2"))
.RespondWith(Response.Create()
.WithWebSocketSubprotocol("v2")
.WithWebSocket(ws => ws
.WithMessage("v2 protocol")
)
);
With State Management
// Scenario-aware behavior
server.Given(Request.Create().WithWebSocketPath("/chat"))
.InScenario("ChatSession")
.WhenStateIs("LoggedIn")
.WillSetStateTo("ChatActive")
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithJsonMessage(new { status = "logged-in" })
)
);
Design Principles
- Fluent First: All builder methods return builder for chaining
- Composable: Messages, transformers, callbacks combine naturally
- Consistent: Follows HTTP mocking patterns and conventions
- Extensible: Partial classes allow feature additions without refactoring
- Testable: Deterministic, controllable message delivery
Integration Points
With Existing Features
// Scenario management
server.Given(Request.Create().WithWebSocketPath("/ws"))
.InScenario("Session")
.WhenStateIs("Connected")
.WillSetStateTo("Active")
.RespondWith(...)
// Priority ordering
server.Given(Request.Create().WithWebSocketPath("/ws"))
.AtPriority(1)
.RespondWith(...)
// Webhooks
server.Given(Request.Create().WithWebSocketPath("/ws"))
.WithWebhook(new Webhook { ... })
.RespondWith(...)
// Admin interface
var mappings = server.MappingModels
.Where(m => m.Response.WebSocket != null)
.ToList()
Testing Patterns
Unit Test Template
[Fact]
public void WebSocket_WithMultipleMessages_MaintainsOrder()
{
// Arrange
var builder = new WebSocketResponseBuilder();
// Act
var response = builder
.WithMessage("First", 0)
.WithMessage("Second", 100)
.Build();
// Assert
Assert.Equal("First", response.Messages[0].BodyAsString);
Assert.Equal("Second", response.Messages[1].BodyAsString);
Assert.Equal(100, response.Messages[1].DelayMs);
}
Integration Test Template
[Fact]
public async Task WebSocket_Client_ReceivesMessages()
{
// Arrange
var server = WireMockServer.Start();
server.Given(Request.Create().WithWebSocketPath("/test"))
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithMessage("Hello")
.WithMessage("World")
)
);
// Act
using var client = new ClientWebSocket();
await client.ConnectAsync(new Uri($"ws://localhost:{server.Port}/test"), CancellationToken.None);
// Receive first message
var buffer = new byte[1024];
var result = await client.ReceiveAsync(buffer, CancellationToken.None);
var message1 = Encoding.UTF8.GetString(buffer, 0, result.Count);
// Assert
Assert.Equal("Hello", message1);
}
Performance Considerations
| Aspect | Impact | Mitigation |
|---|---|---|
| Message queuing | Linear with message count | Use callbacks for large streams |
| Memory | One message per connection | Implement cleanup on close |
| Concurrency | Handle multiple connections | Use async callbacks |
| Delays | Thread pool usage | Use reasonable delays (< 60s) |
Common Issues & Solutions
Issue 1: Message ordering
Problem: Messages delivered out of order
Solution: Use explicit delayMs, avoid concurrent message generation
Issue 2: Connection timeout
Problem: Client disconnects before messages sent
Solution: Reduce message delays, increase test timeout
Issue 3: Memory leak
Problem: Connections not closing properly
Solution: Always call WithClose() or WithAutoClose()
Issue 4: Transformer not working
Problem: Template variables not substituted
Solution: Ensure WithTransformer() is called, check variable syntax
Related Classes & Methods
Request Builder
Request.Create()- Start buildingWithPath(string)- HTTP path or WebSocket pathWithHeader(string, string)- Custom headersUsingGet(),UsingPost(), etc. - HTTP methodsWithWebSocketUpgrade()- Mark as WebSocketWithWebSocketPath(string)- Convenience methodWithWebSocketSubprotocol(string)- Protocol version
Response Builder
Response.Create()- Start buildingWithStatusCode(int)- HTTP statusWithBody(string)- HTTP bodyWithBodyAsJson(object)- JSON responseWithCallback(...)- Dynamic HTTP responseWithWebSocket(...)- WebSocket configurationWithWebSocketMessage(string)- Single messageWithWebSocketCallback(...)- Dynamic WebSocket messagesWithWebSocketTransformer()- Template supportWithWebSocketClose(int, string)- Graceful close
Mapping Builder
Given(IRequestMatcher)- Start mappingAtPriority(int)- Execution priorityInScenario(string)- Scenario groupingWhenStateIs(string)- State conditionWillSetStateTo(string)- State changeWithTitle(string)- Display nameWithDescription(string)- DocumentationWithWebhook(...)- Side effectsRespondWith(IResponseProvider)- Terminal method
Versioning Strategy
Version 1.0
- Basic WebSocket support
- Static messages
- Message delays
- Callback support
- Transformer integration
Version 1.1
- Subprotocol negotiation
- Binary message support
- Auto-close functionality
- WebSocket metrics
Version 2.0
- Streaming responses
- Backpressure handling
- Message compression
- Custom close codes
References
- RFC 6455: The WebSocket Protocol
- RFC 7231: HTTP Semantics and Content
- ASP.NET Core WebSocket Support: https://docs.microsoft.com/en-us/dotnet/api/system.net.websockets
- WireMock.Net Documentation: https://wiremock.org/docs/dotnet
Contact & Support
For questions or contributions regarding WebSocket support:
- Review the comprehensive design documents
- Check the implementation templates for code examples
- Refer to the best practices guide for patterns
- File issues with detailed reproduction steps