A gRPC Java testing tool to easily mock endpoints of gRPC services for IT or Unit testing. The tool follows a similar DSL type of structure to HTTP mocking service WireMock.
- gRPC method stubbing configurable through a fluent Java API
- Headers and request body matchers to determine the correct stub
- Configurable delay for individual responses
- Verifiable method invocations for specific method
- Supported gRPC method types:
- Unary methods
- Server streaming methods
- Client streaming methods
- Bidi stream methods
stubFor(unaryMethod(SimpleServiceGrpc.getUnaryRpcMethod())
.willReturn(response1));
stubFor(unaryMethod(SimpleServiceGrpc.getUnaryRpcMethod())
.withHeader("header-1", "value-1")
.withHeader("header-2", value -> value.startsWith("value"))
.withRequest(expectedRequest)
.willReturn(response(response1)
.withFixedDelay(200)) // first invocation will return this response after 200 ms
.nextWillReturn(response(response2))); // subsequent invocations will return this response
See more examples
stubFor(serverStreamingMethod(SimpleServiceGrpc.getServerStreamingRpcMethod())
.willReturn(responses1, responses2, responses3)); // return one by one with no delay
stubFor(serverStreamingMethod(SimpleServiceGrpc.getServerStreamingRpcMethod())
.withHeader("header-1", "value-1")
.withRequest(req -> req.getRequestMessage().endsWith("1"))
.willReturn(stream(response(responses1).withFixedDelay(200))
.and(response(responses2).withFixedDelay(100))
.and(response(responses3).withFixedDelay(200)))
.nextWillReturn(statusException(Status.NOT_FOUND))); // subsequent invocations will return status exception
See more examples
Stubs for client streaming method calls are selected on receiving first stream request message.
stubFor(clientStreamingMethod(SimpleServiceGrpc.getClientStreamingRpcMethod())
.willReturn(responses1)); // return a response on completed client streaming requests
stubFor(clientStreamingMethod(SimpleServiceGrpc.getClientStreamingRpcMethod())
.withHeader("header-1", "value-1")
.withFirstRequest(req -> req.getRequestMessage().endsWith("1"))
.willReturn(response(responses1).withFixedDelay(200))
.nextWillReturn(statusException(Status.NOT_FOUND))); // subsequent invocations will return status exception
See more examples
Stubs for bidi streaming method calls are selected on receiving first stream request message.
stubFor(bidiStreamingMethod(SimpleServiceGrpc.getBidiStreamingRpcMethod())
.withHeader("header-1", "value-1")
.withFirstRequest(req -> req.getRequestMessage().endsWith("1"))
.willProxyTo(responseObserver -> new StreamObserver<SimpleRequest>() {
@Override
public void onNext(SimpleRequest request) {
SimpleResponse response = SimpleResponse.newBuilder()
.setResponseMessage(request.getRequestMessage())
.build();
responseObserver.onNext(response);
}
@Override
public void onError(Throwable error) {
// handle error
}
@Override
public void onCompleted() {
responseObserver.onCompleted();
}
}));
See more examples
verifyThat(
calledMethod(getUnaryRpcMethod())
.withStatusOk()
.withHeader("header-1", "value-1")
.withRequest(request),
times(3));
verifyThat(getUnaryRpcMethod(), never());
verifyThat(
calledMethod(getClientStreamingRpcMethod())
.withNumberOfRequests(2)
.withFirstRequest(request)
.withRequestAtIndex(1, request2));
See more examples
See example projects
gRPC Mock integrates with Spring-Boot via grpcmock-spring-boot
module.
<dependency>
<groupId>org.grpcmock</groupId>
<artifactId>grpcmock-spring-boot</artifactId>
<version>0.9.5</version>
</dependency>
You have to declare the @AutoConfigureGrpcMock
for the test class to enable gRPC Mock:
@SpringJUnitConfig
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.NONE)
@AutoConfigureGrpcMock
class TestClass {
@Value("${grpcmock.server.port}")
private int grpcMockPort;
private ManagedChannel channel;
@BeforeEach
void setupChannel() {
channel = ManagedChannelBuilder.forAddress("localhost", grpcMockPort)
.usePlaintext()
.build();
}
@AfterEach
void shutdownChannel() {
Optional.ofNullable(channel).ifPresent(ManagedChannel::shutdownNow);
}
}
If the gRPC Mock port is set to 0, then a random port will be selected for the server.
It is the recommended approach to improve test run times.
Once a random port is selected it can be accessed via ${grpcmock.server.port}
property and used in gRPC Channel
creation.
It's also possible to use InProcess server with Spring-Boot
@SpringJUnitConfig
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.NONE)
@AutoConfigureGrpcMock(useInProcessServer = true)
class TestClass {
@Value("${grpcmock.server.name}")
private String inProcessName;
private ManagedChannel channel;
@BeforeEach
void setupChannel() {
channel = InProcessChannelBuilder.forName(inProcessName).build();
}
@AfterEach
void shutdownChannel() {
Optional.ofNullable(channel).ifPresent(ManagedChannel::shutdownNow);
}
}
If you don't set name()
in @AutoConfigureGrpcMock
then it will be generated randomly.
Beware that it's not possible to create two InProcess servers at once so in case of fixed names
you will have to configure a unique name for each spring context. If a random name is used
it can be accessed via ${grpcmock.server.name}
property and used in gRPC Channel
creation.
To remove logging of incoming requests, logging level for GrpcMock should be changed in application-test.yml
:
logging.level.org.grpcmock.GrpcMock: WARN
gRPC Mock integrates with JUnit5 via grpcmock-junit5
module.
<dependency>
<groupId>org.grpcmock</groupId>
<artifactId>grpcmock-junit5</artifactId>
<version>0.9.5</version>
</dependency>
You can integrate gRPC Mock with default configuration for a JUnit5 test via @ExtendWith
annotation:
@ExtendWith(GrpcMockExtension.class)
class TestClass {
private ManagedChannel channel;
@BeforeEach
void setupChannel() {
channel = ManagedChannelBuilder.forAddress("localhost", GrpcMock.getGlobalPort())
.usePlaintext()
.build();
}
@AfterEach
void shutdownChannel() {
Optional.ofNullable(channel).ifPresent(ManagedChannel::shutdownNow);
}
}
For quicker unit test execution you can integrate gRPC Mock using an in-process server via InProcessGrpcMockExtension
extension:
@ExtendWith(InProcessGrpcMockExtension.class)
class TestClass {
private ManagedChannel channel;
@BeforeEach
void setupChannel() {
channel = InProcessChannelBuilder.forName(GrpcMock.getGlobalInProcessName())
.usePlaintext()
.build();
}
@AfterEach
void shutdownChannel() {
Optional.ofNullable(channel).ifPresent(ManagedChannel::shutdownNow);
}
}
Alternatively, you can configure gRPC Mock programmatically using @RegisterExtension
annotation:
class TestClass {
@RegisterExtension
static GrpcMockExtension grpcMockExtension = GrpcMockExtension.builder()
.withPort(0)
.withInterceptor(new MyServerInterceptor())
.build();
private ManagedChannel channel;
@BeforeEach
void setupChannel() {
channel = ManagedChannelBuilder.forAddress("localhost", GrpcMock.getGlobalPort())
.usePlaintext()
.build();
}
@AfterEach
void shutdownChannel() {
Optional.ofNullable(channel).ifPresent(ManagedChannel::shutdownNow);
}
}
In both variants the port for the gRPC Mock server can be retrieved via GrpcMock.getGlobalPort()
. Mapping stubs will be cleared
after each test run and after all tests in the test class are done the server will be shutdown.
To remove logging of incoming requests, logging level for GrpcMock should be changed depending on the logging backend used. E.g.
when using slf4j-simple
backend a file simplelogger.properties
needs to be created in test resouces
with content:
org.slf4j.simpleLogger.log.org.grpcmock.GrpcMock=warn