Categorygo.nhat.io/grpcmock
modulepackage
0.27.0
Repository: https://github.com/nhatthm/grpcmock.git
Documentation: pkg.go.dev

# README

⚠️ From v0.20.0, the project will be rebranded to go.nhat.io/grpcmock. v0.19.0 is the last version with github.com/nhatthm/grpcmock.

gRPC Test Utilities for Golang

GitHub Releases Build Status codecov Go Report Card GoDevDoc Donate

Test gRPC service and client like a pro.

Table of Contents

Prerequisites

  • Go >= 1.21

[table of contents]

Install

go get go.nhat.io/grpcmock

[table of contents]

Usage

Mock a gRPC server

Read more about mocking a gRPC server

[table of contents]

Unary Method

Read more about mocking a Unary Method

package main

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"go.nhat.io/grpcmock"
	xassert "go.nhat.io/grpcmock/assert"
)

func TestGetItems(t *testing.T) {
	t.Parallel()

	expected := &Item{Id: 42, Name: "Item 42"}

	_, d := grpcmock.MockServerWithBufConn(
		grpcmock.RegisterService(RegisterItemServiceServer),
		func(s *grpcmock.Server) {
			s.ExpectUnary("myservice/GetItem").
				WithHeader("locale", "en-US").
				WithPayload(&GetItemRequest{Id: 42}).
				Return(expected)
		},
	)(t)

	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
	defer cancel()

	out := &Item{}

	err := grpcmock.InvokeUnary(ctx,
		"myservice/GetItem",
		&GetItemRequest{Id: 42}, out,
		grpcmock.WithHeader("locale", "en-US"),
		grpcmock.WithContextDialer(d),
		grpcmock.WithInsecure(),
	)

	xassert.EqualMessage(t, expected, out)
	assert.NoError(t, err)
}

[table of contents]

Client-Stream Method

Read more about mocking a Client-Stream Method

package main

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"go.nhat.io/grpcmock"
	xassert "go.nhat.io/grpcmock/assert"
)

func TestCreateItems(t *testing.T) {
	t.Parallel()

	expected := &CreateItemsResponse{NumItems: 1}

	_, d := grpcmock.MockServerWithBufConn(
		grpcmock.RegisterService(RegisterItemServiceServer),
		func(s *grpcmock.Server) {
			s.ExpectClientStream("myservice/CreateItems").
				WithPayload([]*Item{{Id: 42}}).
				Return(expected)
		},
	)(t)

	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
	defer cancel()

	out := &CreateItemsResponse{}
	err := grpcmock.InvokeClientStream(ctx, "myservice/CreateItems",
		grpcmock.SendAll([]*Item{{Id: 42}}), out,
		grpcmock.WithContextDialer(d),
		grpcmock.WithInsecure(),
	)

	xassert.EqualMessage(t, expected, out)
	assert.NoError(t, err)
}

[table of contents]

Server-Stream Method

Read more about mocking a Server-Stream Method

package main

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"go.nhat.io/grpcmock"
	xassert "go.nhat.io/grpcmock/assert"
)

func TestListItems(t *testing.T) {
	t.Parallel()

	expected := []*Item{
		{Id: 41, Name: "Item 41"},
		{Id: 42, Name: "Item 42"},
	}

	_, d := grpcmock.MockServerWithBufConn(
		grpcmock.RegisterService(RegisterItemServiceServer),
		func(s *grpcmock.Server) {
			s.ExpectServerStream("myservice/ListItems").
				WithPayload(&ListItemsRequest{}).
				Return(expected)
		},
	)(t)

	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
	defer cancel()

	actual := make([]*Item, 0)

	err := grpcmock.InvokeServerStream(ctx,
		"myservice/ListItems",
		&ListItemsRequest{},
		grpcmock.RecvAll(&actual),
		grpcmock.WithContextDialer(d),
		grpcmock.WithInsecure(),
	)

	assert.NoError(t, err)
	assert.Len(t, actual, len(expected))

	for i := 0; i < len(expected); i++ {
		xassert.EqualMessage(t, expected[i], actual[i])
	}
}

[table of contents]

Bidirectional-Stream Method

Read more about mocking a Bidirectional-Stream Method

package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"go.nhat.io/grpcmock"
	xassert "go.nhat.io/grpcmock/assert"
	"google.golang.org/grpc"
)

func TestTransformItems(t *testing.T) {
	t.Parallel()

	expected := []*Item{
		{Id: 41, Name: "Item 41"},
		{Id: 42, Name: "Item 42"},
	}

	_, d := grpcmock.MockServerWithBufConn(
		grpcmock.RegisterService(RegisterItemServiceServer),
		func(s *grpcmock.Server) {
			s.ExpectBidirectionalStream("myservice/TransformItems").
				Run(func(ctx context.Context, s grpc.ServerStream) error {
					for {
						item := &Item{}
						err := s.RecvMsg(item)

						if errors.Is(err, io.EOF) {
							return nil
						}

						if err != nil {
							return err
						}

						item.Name = fmt.Sprintf("Modified #%d", item.Id)

						if err := s.SendMsg(item); err != nil {
							return err
						}
					}
				})
		},
	)(t)

	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
	defer cancel()

	in := []*Item{
		{Id: 41, Name: "Item 41"},
		{Id: 42, Name: "Item 42"},
	}

	actual := make([]*Item, 0)

	err := grpcmock.InvokeBidirectionalStream(ctx,
		"myservice/TransformItems",
		grpcmock.SendAndRecvAll(in, &actual),
		grpcmock.WithContextDialer(d),
		grpcmock.WithInsecure(),
	)

	assert.NoError(t, err)
	assert.Len(t, actual, len(expected))

	for i := 0; i < len(expected); i++ {
		xassert.EqualMessage(t, expected[i], actual[i])
	}
}

[table of contents]

Invoke a gRPC method

Unary Method

package main

import (
	"context"
	"time"

	"go.nhat.io/grpcmock"
	"google.golang.org/grpc/test/bufconn"
)

func getItem(l *bufconn.Listener, id int32) (*Item, error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
	defer cancel()

	out := &Item{}
	err := grpcmock.InvokeUnary(ctx, "myservice/GetItem",
		&GetItemRequest{Id: id}, out,
		grpcmock.WithHeader("Locale", "en-US"),
		grpcmock.WithBufConnDialer(l),
		grpcmock.WithInsecure(),
	)

	return out, err
}

[table of contents]

Client-Stream Method

package main

import (
	"context"
	"time"

	"go.nhat.io/grpcmock"
	"google.golang.org/grpc/test/bufconn"
)

func createItems(l *bufconn.Listener, items []*Item) (*CreateItemsResponse, error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
	defer cancel()

	out := &CreateItemsResponse{}
	err := grpcmock.InvokeClientStream(ctx, "myservice/CreateItems",
		grpcmock.SendAll(items), out,
		grpcmock.WithBufConnDialer(l),
		grpcmock.WithInsecure(),
	)

	return out, err
}

Or with a custom handler

package main

import (
	"context"
	"time"

	"go.nhat.io/grpcmock"
	"google.golang.org/grpc"
	"google.golang.org/grpc/test/bufconn"
)

func createItems(l *bufconn.Listener, items []*Item) (*CreateItemsResponse, error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
	defer cancel()

	out := &CreateItemsResponse{}
	err := grpcmock.InvokeClientStream(ctx, "myservice/CreateItems",
		func(s grpc.ClientStream) error {
			// Handle the stream here.
			return nil
		},
		out,
		grpcmock.WithBufConnDialer(l),
		grpcmock.WithInsecure(),
	)

	return out, err
}

[table of contents]

Server-Stream Method

package main

import (
	"context"
	"time"

	"go.nhat.io/grpcmock"
	"google.golang.org/grpc/test/bufconn"
)

func listItems(l *bufconn.Listener) ([]*Item, error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
	defer cancel()

	out := make([]*Item, 0)
	err := grpcmock.InvokeServerStream(ctx, "myservice/ListItems",
		&ListItemsRequest{},
		grpcmock.RecvAll(&out),
		grpcmock.WithBufConnDialer(l),
		grpcmock.WithInsecure(),
	)

	return out, err
}

Or with a custom handler

package main

import (
	"context"
	"time"

	"go.nhat.io/grpcmock"
	"google.golang.org/grpc"
	"google.golang.org/grpc/test/bufconn"
)

func listItems(l *bufconn.Listener) ([]*Item, error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
	defer cancel()

	out := make([]*Item, 0)
	err := grpcmock.InvokeServerStream(ctx, "myservice/ListItems",
		&ListItemsRequest{},
		func(s grpc.ClientStream) error {
			// Handle the stream here.
			return nil
		},
		grpcmock.WithBufConnDialer(l),
		grpcmock.WithInsecure(),
	)

	return out, err
}

[table of contents]

Bidirectional-Stream Method

package main

import (
	"context"
	"time"

	"go.nhat.io/grpcmock"
	"google.golang.org/grpc/test/bufconn"
)

func transformItems(l *bufconn.Listener, in []*Item) ([]*Item, error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
	defer cancel()

	out := make([]*Item, 0)
	err := grpcmock.InvokeBidirectionalStream(ctx, "myservice/TransformItems",
		grpcmock.SendAndRecvAll(in, &out),
		grpcmock.WithBufConnDialer(l),
		grpcmock.WithInsecure(),
	)

	return out, err
}

Or with a custom handler

package main

import (
	"context"
	"time"

	"go.nhat.io/grpcmock"
	"google.golang.org/grpc"
	"google.golang.org/grpc/test/bufconn"
)

func transformItems(l *bufconn.Listener, in []*Item) ([]*Item, error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
	defer cancel()

	out := make([]*Item, 0)
	err := grpcmock.InvokeBidirectionalStream(ctx, "myservice/TransformItems",
		func(s grpc.ClientStream) error {
			// Handle the stream here.
			return nil
		},
		grpcmock.WithBufConnDialer(l),
		grpcmock.WithInsecure(),
	)

	return out, err
}

[table of contents]

Donation

If this project help you reduce time to develop, you can give me a cup of coffee :)

[table of contents]

Paypal donation

paypal

       or scan this

[table of contents]

# Packages

Package assert provides assertions for grpc.
Package errors provides predefined errors.
Package format formats the requests.
Package invoker constructs a grpc request and executes it.
Package matcher provides custom matchers for gRPC requests.
No description provided by the author
Package must panic on error.
Package planner provides functionalities for deciding what expectation matches the given request.
Package reflect provides a simple reflection on the grpc server.
Package request provides all the expectations for an RPC method.
Package service provides service definition.
Package stream provides functions to send or receive messages using a grpc stream.
Package streamer provides functionalities to work with server-side RPC stream.
Package test provides helpers for testing grpcmock.
Package value provides functionalities to convert a generic value to string.

# Functions

ChainStreamInterceptor is a wrapper of google.golang.org/grpc.ChainStreamInterceptor().
ChainUnaryInterceptor is a wrapper of google.golang.org/grpc.ChainUnaryInterceptor().
ConnectionTimeout is a wrapper of google.golang.org/grpc.ConnectionTimeout().
Creds is a wrapper of google.golang.org/grpc.Creds().
FindServerMethod finds a method in the given server.
InitialConnWindowSize is a wrapper of google.golang.org/grpc.InitialConnWindowSize().
InitialWindowSize is a wrapper of google.golang.org/grpc.InitialWindowSize().
InTapHandle is a wrapper of google.golang.org/grpc.InTapHandle().
InvokeBidirectionalStream invokes a bidirectional-stream method.
InvokeClientStream invokes a client-stream method.
InvokeServerStream invokes a server-stream method.
InvokeUnary invokes a unary method.
KeepaliveEnforcementPolicy is a wrapper of google.golang.org/grpc.KeepaliveEnforcementPolicy().
KeepaliveParams is a wrapper of google.golang.org/grpc.KeepaliveParams().
MatchClientStreamMsgCount matches a number of messages.
MaxConcurrentStreams is a wrapper of google.golang.org/grpc.MaxConcurrentStreams().
MaxHeaderListSize is a wrapper of google.golang.org/grpc.MaxHeaderListSize().
MaxRecvMsgSize is a wrapper of google.golang.org/grpc.MaxRecvMsgSize().
MaxSendMsgSize is a wrapper of google.golang.org/grpc.MaxSendMsgSize().
MockServer starts a new mocked server and ensures all the expectations were met at the end of the test.
MockServerWithBufConn starts a new mocked server with bufconn and ensures all the expectations were met at the end of the test.
MockUnstartedServer mocks the server and ensures all the expectations were met at the end of the test.
NewServer creates mocked server.
NewUnstartedServer returns a new Server but doesn't start it.
NoOpT initiates a new T that does nothing.
ReadBufferSize is a wrapper of google.golang.org/grpc.ReadBufferSize().
RecvAll reads everything from the stream and put into the output.
RegisterService registers a new service using the generated register function.
RegisterServiceFromInstance registers a new service using the generated server interface.
RegisterServiceFromMethods registers a new service using service.Method definition.
SendAll sends everything to the stream.
SendAndRecvAll sends and receives messages to and from grpc server in turn until server sends the io.EOF.
StatsHandler is a wrapper of google.golang.org/grpc.StatsHandler().
StreamInterceptor is a wrapper of google.golang.org/grpc.ChainStreamInterceptor().
UnaryInterceptor is a wrapper of google.golang.org/grpc.ChainUnaryInterceptor().
UnknownServiceHandler is a wrapper of google.golang.org/grpc.UnknownServiceHandler().
WithAddress sets server address.
WithBufConnDialer sets a *bufconn.Listener as the context dialer.
WithCallOptions sets call options.
WithContextDialer sets a context dialer to create connections.
WithDialOptions sets dial options.
WithHeader sets request header.
WithHeaders sets request header.
WithInsecure disables transport security for the connections.
WithListener sets the listener.
WithPlanner sets the expectations' planner.
WithPort sets server address port.
WriteBufferSize is a wrapper of google.golang.org/grpc.WriteBufferSize().

# Structs

Server wraps a grpc server and provides mocking functionalities.

# Interfaces

BidirectionalStreamExpectation represents the expectation for a client-stream request.
ClientStreamExpectation represents the expectation for a client-stream request.
ServerStreamExpectation represents the expectation for a server-stream request.
ServerStreamHandler handles a server-stream request step by step.
T is an interface wrapper around *testing.T.
UnaryExpectation represents the expectation for a unary request.

# Type aliases

ClientStreamHandler handles a client stream.
ContextDialer is to set up the dialer.
InvokeOption sets invoker config.
ServerMocker is a constructor to create a new mocked server.
ServerMockerWithContextDialer starts a new mocked server with a bufconn and returns it as a context dialer for the grpc.DialOption.
ServerOption sets up the mocked server.