Key Takeaways
- Mocking gRPC services allows you to validate gRPC integration code during your tests while avoiding common pitfalls such as unreliable sandboxes, version mismatches, and complex test data setup requirements.
- The WireMock gRPC extension supports familiar HTTP stubbing patterns to be used with gRPC-based integration points.
- WireMock’s Spring Boot integration enables dynamic port allocation and automatic configuration injection, eliminating the need for fixed ports and enabling parallel test execution while making test infrastructure more maintainable and scalable.
- API mocking can accelerate and simplify many testing activities, but complex systems contain failure modes that can only realistically be discovered during integration testing, meaning this is still an essential practice.
- While basic unidirectional streaming methods can be mocked, more work is still needed for WireMock to support advanced testing patterns.
When your code depends on numerous external services, end-to-end testing is often prohibitively slow and complex due to issues such as unavailable sandboxes, unstable environments, and difficulty setting up necessary test data. Additionally, running many hundreds or thousands of automated tests against sandbox API implementations can result in very long build/pipeline run times.
Mock-based testing offers a practical alternative that balances comprehensive testing with execution speed. Plenty of tools exist that support mocking of REST APIs, but gRPC is much less widely supported despite its growing popularity, and much less has been written about this problem. In this article, we’ll show you how you can use familiar JVM tools - Spring Boot, gRPC, and WireMock together. We’ll also discuss some of the tradeoffs and potential gotchas to consider when testing against mocked gRPC APIs.
Mocks, integration testing and tradeoffs
API mocking involves a tradeoff - a faster, more deterministic and cheaper-to-create API implementation at the expense of some realism. This works because many types of tests (and thus risks managed) do not rely on nuances of API behaviour we’ve chosen not to model in a mock, so we get the benefits without losing out on any vital feedback.
However, some types of defects will only surface as the result of complex interactions between system components that aren’t captured in mock behaviour, so some integration testing is still necessary in a well-rounded test strategy.
The good news is that this can be a small fraction of the overall test suite, provided that tests are created with an explicit judgement about whether they’re expected to reveal the type of complex integration issue mentioned above.
The tools we’ll be using
First, let’s define the terms we’ll be using - feel free to skip down to the next section if you’re already familiar!
gRPC is a modern network protocol built on HTTP/2 and leveraging Protocol Buffers (Protobuf) for serialization. It is often used in microservice architectures where network efficiency and low latency are important.
Spring Boot is a framework within the Spring ecosystem that simplifies Java application development through convention-over-configuration principles and intelligent defaults. It enables developers to focus on business logic while maintaining the flexibility to customize when needed.
WireMock is an open source API simulation tool that enables developers to create reliable, deterministic test environments by providing configurable mock implementations of external service dependencies.
Why mock gRPC in Spring Boot?
While building an app or service that calls out to gRPC services, you need there to be something handling those calls during development. There are a few options here:
- Call out to a real service implementation sandbox.
- Mock the interface that defines the client using an object mocking tool like Mockito.
- Mock the gRPC service locally and configure your app to connect to this during dev and test.
The first option can often be impractical for a number of reasons:
- The sandbox is slow or unreliable.
- It’s running the wrong version of the service.
- Setting up the correct test data is difficult.
- The service isn’t even built yet.
The second option avoids the above issues but significantly reduces the effectiveness of your tests due to the fact that not all of the gRPC-related code gets executed. gRPC is a complex protocol with a number of failure modes, and ideally, we want to discover these in our automated tests rather than in staging, or even worse, production.
So this leaves mocking the gRPC service over the wire, which brings the advantages of executing the gRPC integration code during your tests while avoiding the pitfalls of relying on an external sandbox. This is what we’ll explore in the remainder of this article.
Where it gets tricky
Setting up mock services for testing often introduces configuration complexity that can undermine the benefits of the mocking approach itself. A particular pain point arises from the practice of using fixed port numbers for mock servers in order to be able to configure the local environment to address them. Having fixed ports prevents parallelization of the tests, making it difficult to scale out test runners as the test suite grows. Fixed ports can also cause errors in some shared tenancy CI/CD environments.
WireMock's Spring Boot integration addresses these challenges through dynamic port allocation and configuration injection. By automatically assigning available ports at runtime and seamlessly injecting these values into the application context, it eliminates the need for manual port management while enabling parallel test execution. This functionality, combined with a declarative annotation-based configuration system, significantly reduces setup complexity and makes the testing infrastructure more maintainable.
The second problem is that while API mocking tools have traditionally served REST and SOAP protocols well, gRPC support has remained notably scarce. To close this gap, we’ll use the newly released WireMock gRPC extension. The extension is designed to bring the same powerful mocking capabilities to gRPC-based architectures while preserving the familiar stubbing patterns that developers use for traditional HTTP testing.
Putting it all together
The following is a step-by-step guide to building a Spring Boot app that calls a simple gRPC "echo" service and returns the message contained in the request. If you want to check out and run some working code right away, all the examples in this article are taken from this demo project.
We’ll use Gradle as our build tool, but this can quite easily be adapted to Maven if necessary.
Step 1: Create an empty Spring Boot app
Let’s start with a standard Spring Boot application structure. We'll use Spring Initializr to scaffold our project with the essential dependencies:
plugins { id 'org.springframework.boot' version '3.2.0' id 'io.spring.dependency.management' version '1.1.4' id 'java' } group = 'org.wiremock.demo' version = '0.0.1-SNAPSHOT' dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test'Step 2: Add gRPC dependencies and build tasks
Since gRPC requires service interfaces to be fully defined in order to work, we have to do a few things at build time:
● Generate Java stubs, which will be used when setting up stubs in WireMock.
● Generate a descriptor (.dsc) file, which will be used by WireMock when serving mocked responses.
Add the gRPC starter module
implementation 'org.springframework.grpc:spring-grpc-spring-boot-starter:0.2.0'Add the Google Protobuf libraries and Gradle plugin
plugins { id "com.google.protobuf" version "0.9.4" }In the dependencies section:
protobuf 'com.google.protobuf:protobuf-java:3.18.1' protobuf 'io.grpc:protoc-gen-grpc-java:1.42.1'Ensure we’re generating both the descriptor and Java sources:
protobuf { protoc { artifact = "com.google.protobuf:protoc:3.24.3" } plugins { grpc { artifact = "io.grpc:protoc-gen-grpc-java:$versions.grpc" } } generateProtoTasks { all()*.plugins { grpc { outputSubDir = 'java' } } all().each { task -> task.generateDescriptorSet = true task.descriptorSetOptions.includeImports = true task.descriptorSetOptions.path = "$projectDir/src/test/resources/grpc/services.dsc" } } }Add a src/main/proto folder and add a protobuf service description file in there:
package org.wiremock.grpc; message EchoRequest { string message = 1; } message EchoResponse { string message = 1; } service EchoService { rpc echo(EchoRequest) returns (EchoResponse); }After adding this, we can generate the Java sources and descriptor:
./gradlew generateProto generateTestProtoYou should now see a file called services.dsc under src/test/resources/grpc and some generated sources under build/generated/source/proto/main/java/org/wiremock/grpc.
Step 3: Configure application components for gRPC integration
Now that we have Java stubs generated, we can write some code that depends on them.
We’ll start by creating a REST controller that takes a GET to /test-echo and calls out to the echo gRPC service:
@RestController public class MessageController { @Autowired EchoServiceGrpc.EchoServiceBlockingStub echo; @GetMapping("/test-echo") public String getEchoedMessage(@RequestParam String message) { final EchoResponse response = echo.echo(EchoServiceOuterClass.EchoRequest.newBuilder().setMessage(message).build()); return response.getMessage(); } }Then we’ll configure a Spring Boot application that initialises the gRPC service bean (that we need so it can be injected into the REST controller):
@SpringBootApplication public class SpringbootGrpcApplication { @Bean EchoServiceGrpc.EchoServiceBlockingStub echo(GrpcChannelFactory channels) { return EchoServiceGrpc.newBlockingStub(channels.createChannel("local").build()); } public static void main(String[] args) { SpringApplication.run(SpringbootGrpcApplication.class, args); } }Step 4: Set up an integration test class
Now we can start to build some tests that will rely on gRPC mocking.
First, we need to configure a few things in the test class.
@SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringbootGrpcApplication.class ) @EnableWireMock({ @ConfigureWireMock( name = "greeting-service", portProperties = "greeting-service.port", extensionFactories = { Jetty12GrpcExtensionFactory.class } ) }) class SpringbootGrpcApplicationTests { @InjectWireMock("greeting-service") WireMockServer echoWireMockInstance; WireMockGrpcService mockEchoService; @LocalServerPort int serverPort; RestClient client; @BeforeEach void init() { mockEchoService = new WireMockGrpcService( new WireMock(echoWireMockInstance), EchoServiceGrpc.SERVICE_NAME ); client = RestClient.create(); } }There’s quite a bit happening here:
- The @SpringBootTest annotation starts our Spring application, and we’ve configured it to use a random port (which we need if we’re going to ultimately parallelize our test suite).
- @EnableWireMock adds the WireMock integration to the test, with a single instance defined by the @ConfigureWireMock nested annotation.
- The instance is configured to use Jetty12GrpcExtensionFactory, which is the gRPC WireMock extension.
- @InjectWireMock injects the WireMockServer instance into the test.
- The WireMockGrpcService, which is instantiated in the init() method, is the gRPC stubbing DSL. It wraps the WireMock instance, taking the name of the gRPC service to be mocked.
- We’ll use the RestClient to make test requests to the Spring app, using the port number indicated by @LocalServerPort.
Now we’re ready to write a test. We’ll configure the gRPC echo service to return a simple, canned response and then make a request to the app’s REST interface and check that the value is passed through as we’d expect:
@Test void returns_message_from_grpc_service() { mockEchoService.stubFor( method("echo") .willReturn(message( EchoResponse.newBuilder() .setMessage("Hi Tom") ))); String url = "http://localhost:" + serverPort + "/test-echo?message=blah"; String result = client.get() .uri(url) .retrieve() .body(String.class); assertThat(result, is("Hi Tom")); }The interesting part here is the first statement in the test method. This is essentially saying "when the echo gRPC method is called, return an EchoResponse with a message value of ‘Hi Tom’".
Note how we’re taking advantage of the Java model code generated by the protoc tool (via the Gradle build). WireMock’s gRPC DSL takes a generated model and associated builder objects, which gives you a type-safe way of expressing response bodies and expected request bodies.
The benefit of this is that we a) get IDE auto-completion when we’re building body objects in our stubbing code, and b) if the model changes (due to a change in the .proto files), it will immediately get flagged by the compiler.
Step 5: Add dynamic responses
There are some cases where working with generated model classes can be overly constraining, so WireMock also supports working with request and response bodies as JSON.
For instance, suppose we want to echo the message sent in the request to the response rather than just returning a canned value. We can do this by templating a JSON string as the response body, where the structure of the JSON matches the response body type defined in the protobuf file:
@Test void returns_dynamic_message_from_grpc_service() { mockEchoService.stubFor( method("echo").willReturn( jsonTemplate( "{\"message\":\"{{jsonPath request.body '$.message'}}\"}" ))); String url = "http://localhost:" + serverPort + "/test-echo?" + "message=my-messsage"; String result = client.get() .uri(url) .retrieve() .body(String.class); assertThat(result, is("my-messsage")); }We can also use all of WireMock’s built-in matchers against the JSON representation of the request:
mockEchoService.stubFor( method("echo").withRequestMessage( matchingJsonPath("$.message", equalTo("match-this")) ) .willReturn( message(EchoResponse.newBuilder().setMessage("matched!")) ));Current limitations
At present, WireMock’s gRPC extension has quite limited support for unidirectional streaming, allowing only a single request event to trigger a stub match, and only a single event to be returned in a streamed response. Bidirectional streaming methods are currently not supported at all.
In both cases, these are due to limitations in WireMock’s underlying model for requests and responses, and the intention is to address this in the upcoming 4.x release of the core library.
Of course, this is all open source, so any contributions to this effort would be very welcome!
Additionally, only a limited range of standard Protobuf features have been tested with the extension, and occasionally, incompatibilities are discovered, for which issues and PRs are also gratefully received.
And we’re done!
If you’ve made it this far, you hopefully now have a good idea of how gRPC mocking can be used to support your Spring Boot integration tests.
Note that while this is the approach we recommend, there is always a tradeoff involved: mocking cannot capture all real-world failure modes. We would recommend using tools such as contract testing or ongoing validation against the real API to increase the reliability of your tests.
There’s more that we haven’t shown here, including errors, faults, verifications and hot reload. The gRPC extension’s documentation and tests are great places to look if you want to learn more.
For more information about the WireMock Spring Boot integration, see this page.