Statically typed gRPC mocking for WireMock.Net using generated Google.Protobuf contracts.
Request.Create()
.WithBodyAsGoogleProtobuf(new HelloRequest { Name = "Stepan" });
Response.Create()
.WithBodyAsGoogleProtobuf(
new HelloReply { Message = "Hello, Stepan!" });No ProtoDefinition, message type strings, JSON conversion, or manually constructed binary payloads.
The standard WireMock.Net protobuf integration requires loading .proto definitions and referring to protobuf message types separately from your generated C# contracts.
This package uses the generated IMessage<T> types you already have to write less configuration and do no duplicated schema setup:
- compile-time type safety;
- native
Google.Protobufserialization; - exact-message and predicate request matching;
When a contract changes, outdated mocks stop compiling instead of failing at runtime.
dotnet add package WireMock.Grpc.ProtobufThe package targets .NET 8
Assuming you have some greet.proto file that generates HelloRequest, HelloReply, and Greeter.GreeterClient for instance:
syntax = "proto3";
option csharp_namespace = "Greeting.Contracts";
package greet;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}And assuming you also have the required packages added with protobuf compilation settings to your test project:
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="WireMock.Net" Version="2.13.0" />
<PackageReference Include="WireMock.Grpc.Protobuf" Version="1.0.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.80.0" />
<PackageReference Include="Grpc.Core" Version="2.46.6"/>
<PackageReference Include="Grpc.Tools" Version="2.82.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Protobuf Include=".\test.proto" GrpcServices="Client" />
</ItemGroup>
</Project>Start WireMock.Net with HTTP/2 enabled and configure the expected request and response using the WithBodyAsGoogleProtobuf extension methods:
using Greeting.Contracts;
using Grpc.Net.Client;
using WireMock.Net.Google.Protobuf.Request;
using WireMock.Net.Google.Protobuf.Response;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using WireMock.Settings;
using var server = WireMockServer.Start(
new WireMockServerSettings
{
UseHttp2 = true
});
var request = new HelloRequest
{
Name = "Stepan"
};
var expectedResponse = new HelloReply
{
Message = "Hello, Stepan!"
};
server
.Given(
Request.Create()
.UsingPost()
.WithHttpVersion("2")
.WithPath("/greet.Greeter/SayHello")
.WithBodyAsGoogleProtobuf(request))
.RespondWith(
Response.Create()
.WithHeader("Content-Type", "application/grpc")
.WithTrailingHeader("grpc-status", "0")
.WithBodyAsGoogleProtobuf(expectedResponse));
using var channel = GrpcChannel.ForAddress(server.Url!);
var client = new Greeter.GreeterClient(channel);
var actualResponse = await client.SayHelloAsync(request);
Assert.Equal(expectedResponse, actualResponse);Match only the fields relevant to your test:
Request.Create()
.WithBodyAsGoogleProtobuf<HelloRequest>(
request =>
request.Name.StartsWith("Step") &&
request.Name.Length >= 5);This keeps request matching strongly typed without comparing the entire protobuf message.
Do not call WithTransformer() after WithBodyAsGoogleProtobuf(). The response is already a binary gRPC frame, and text transformation may corrupt it.
WireMock.Net must also run with HTTP/2 enabled:
new WireMockServerSettings
{
UseHttp2 = true
};Distributed under the MIT License.