gRPC Mock

repository·master·Indexed 41 days ago

https://github.com/fadelis/grpcmock

A Java testing tool for mocking gRPC service endpoints in unit and integration tests. It features a fluent DSL for stubbing unary, server-streaming, client-streaming, and bidirectional streaming methods. It provides dedicated integrations for JUnit5 and Spring Boot, including support for in-process servers, dynamic port assignment, and TLS transport security.

Tokens
3.3K
Snippets
9
Records
12
Agent score
100%

What's inside gRPC Mock

  1. Handle gRPC Mock in multithreaded test setups

    master

    Because GrpcMock uses ThreadLocal for its static API, calling stubFor() or verifyThat() from a thread different from the one that initialized the test will result in UNIMPLEMENTED errors.

    To fix this, you must use an explicit GrpcMock instance instead of static methods:

    Option 1: Use the instance directly

    • In Spring Boot: @Autowire the GrpcMock instance.
    • In other setups: Create it via new GrpcMock.Builder()...build().start().
    • Call grpcMock.register(...) and grpcMock.verifyThat(...) directly.

    Option 2: Use configureFor(instance)

    • Call configureFor(grpcMockInstance) at the start of your test method or in @BeforeEach. This allows you to continue using the static stubFor() and verifyThat() DSL.
    // Option 1: Direct instance usage
    @Test
    void should_test_something() {
      grpcMock.register(unaryMethod(SimpleServiceGrpc.getUnaryRpcMethod())
            .willReturn(response));
      
      grpcMock.verifyThat(calledMethod(SimpleServiceGrpc.getUnaryRpcMethod()).build(), CountMatcher.once());
    }
    
    // Option 2: Using configureFor
    @Test
    void should_test_something() {
      configureFor(grpcMock);
      stubFor(unaryMethod(SimpleServiceGrpc.getUnaryRpcMethod())
            .willReturn(response));
    }
  2. Use gRPC Mock with JUnit5

    master

    This example demonstrates how to integrate grpcmock into a JUnit5 testing environment. It allows you to mock gRPC services and verify calls made from within JUnit5 tests, facilitating easier testing of gRPC clients or services that depend on external gRPC dependencies.

    # gRPC Mock JUnit5 Example
    
    A simple example of gRPC Mock integration in JUnit5, where a gRPC service is called from JUnit5 test.
  3. Integrate gRPC Mock with JUnit5

    master

    Use the grpcmock-junit5 dependency to integrate with JUnit5. You have three main ways to configure it:

    1. Default Extension: Use @ExtendWith(GrpcMockExtension.class). Access the server port via GrpcMock.getGlobalPort().
    2. In-Process Extension: Use @ExtendWith(InProcessGrpcMockExtension.class) for faster execution. Access the name via GrpcMock.getGlobalInProcessName().
    3. Programmatic Configuration: Use @RegisterExtension to build a custom extension with specific ports or interceptors.

    Note: Stubs are cleared after each test, and the server shuts down after all tests in the class are finished.

    <dependency>
      <groupId>org.grpcmock</groupId>
      <artifactId>grpcmock-junit5</artifactId>
      <version>1.0.0</version>
    </dependency>
  4. Integrate gRPC Mock with Spring Boot

    master

    To use gRPC Mock in Spring Boot, add the grpcmock-spring-boot dependency. Enable it by annotating your test class with @AutoConfigureGrpcMock.

    Port Configuration:

    • Setting the port to 0 (default recommended) selects a random port, which improves test speed and avoids conflicts. Access this port via the ${grpcmock.server.port} property to build your ManagedChannel.
    • To use an In-Process server, use @AutoConfigureGrpcMock(useInProcessServer = true). Access the in-process name via ${grpcmock.server.name}.

    Example (Random Port):

    @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();
      }
    }
    <dependency>
      <groupId>org.grpcmock</groupId>
      <artifactId>grpcmock-spring-boot</artifactId>
      <version>1.0.0</version>
    </dependency>
  5. Integrate GrpcMock with Spring Boot

    master

    GrpcMock can be automatically integrated into a Spring application context using the @AutoConfigureGrpcMock annotation (implied by the customizer logic). This integration handles the full lifecycle of the GrpcMock server: creation, starting before beans are initialized, and stopping when the context closes.

    When integrated, the following properties are automatically added to the Spring Environment, allowing them to be injected via @Value:

    • grpcmock.server.port: The actual port the server is listening on.
    • grpcmock.server.name: The in-process server name (if using an in-process server).
    • grpcmock.server.port-dynamic: A boolean indicating if the port was assigned dynamically (true if the configured port was 0 and no in-process name was provided).

    This ensures that the port is available for injection without race conditions.

  6. Stub Unary gRPC methods

    master

    Use unaryMethod() within stubFor() to mock unary gRPC calls. You can match requests using headers (with exact values or predicates) and request bodies. You can also configure response delays and chain multiple responses for subsequent calls using nextWillReturn().

    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
  7. Stub Server streaming gRPC methods

    master

    Use serverStreamingMethod() to mock methods that stream responses to the client. You can provide multiple responses that are returned one by one. You can also use stream() to wrap individual responses with specific delays. Subsequent calls to the stub can be configured to return a statusException().

    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)));
  8. Verify gRPC method invocations

    master

    Use verifyThat() to assert that specific methods were called with expected parameters and frequency.

    • Use calledMethod(method) to specify the method being verified.
    • Use .withStatusOk(), .withHeader(), or .withRequest() to match the invocation details.
    • Use times(n) or never() to verify the count.
    • For streaming, use .withNumberOfRequests(n) and .withRequestAtIndex(index, request) to verify specific messages in a stream.
    // Verify exact count and parameters
    verifyThat(
        calledMethod(getUnaryRpcMethod())
            .withStatusOk()
            .withHeader("header-1", "value-1")
            .withRequest(request),
        times(3));
    
    // Verify method was never called
    verifyThat(getUnaryRpcMethod(), never());
    
    // Verify client streaming request sequence
    verifyThat(
        calledMethod(getClientStreamingRpcMethod())
            .withNumberOfRequests(2)
            .withFirstRequest(request)
            .withRequestAtIndex(1, request2));
  9. Stub Bidi streaming gRPC methods

    master

    For bidirectional streaming, stubs are selected on the first request message. Use bidiStreamingMethod() and willProxyTo() to implement custom logic. The willProxyTo method provides a responseObserver that you can use to react to incoming requests and send back responses manually.

    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();
            }
        }));
  10. Stub Client streaming gRPC methods

    master

    Stubs for client streaming methods are selected based on the first message received in the stream. Use clientStreamingMethod() and withFirstRequest() to define matching criteria for the initial message.

    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)));
  11. Configure GrpcMock Spring Boot options

    master

    When using the Spring Boot integration, you can configure the GrpcMock server via the AutoConfigureGrpcMock annotation. The following configuration options are supported:

    • In-Process Server: Use useInProcessServer() to switch from a network-based server to an in-process server.
    • Server Name: If using an in-process server, provide a name() to identify it.
    • Port: Specify a fixed port using port(). Use 0 for dynamic port assignment.
    • Interceptors: Provide an array of interceptors() (classes extending ServerInterceptor) which will be instantiated using their default no-args constructor.
    • Executor:
      • Provide a executorBeanName() to use an existing Spring bean of type Executor.
      • Alternatively, specify executorThreadCount() to create a new fixed thread pool.
    • Transport Security: Provide certChainFile() and privateKeyFile() to enable TLS. Both must be provided if one is specified.

    Note: If certChainFile and privateKeyFile are both provided, the server will use transport security.