gRPC Server stub
This article demonstrates an alternate way to test the gRPC application. In my article gRPC client stub, I demonstrated how can we stub gRPC client for our testing. In this article, the focus is on stubbing the gRPC server and letting the client make a connection to the server and retrieve the desired response(s) in our test.
Testing by stubbing gRPC Server.
I strongly recommend to please refer to the article gRPC client stub, to view the proto file, and command to generate the source files from the proto and gRPC client code. To test it with the stub, let’s quickly have a glance at autogenerated “helloworld_grpc.pb.go”.
The server will have below 4 main lines to register the server and start listening on the desired port.
lisAddr, _ := net.Listen("tcp", "localhost:50051")
grpcServer := grpc.NewServer()
helloworld.RegisterGreeterServer(grpcServer, &helloworld.UnimplementedGreeterServer{})
grpcServer.Serve(lisAddr)
We actually need to follow the same above 4 steps to start a gRPC stub server. Let us start by writing our server stub first.
package helloworld
import (
"context"
)
type StubGreeterServer struct {
Reply *HelloReply
}
func (gc StubGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) {
return gc.Reply, nil
}
func (gc StubGreeterServer) mustEmbedUnimplementedGreeterServer() {
// do nothing
}
Let's break down the above file.
- We have created a StubGreeterServer corresponding to UnimplementedGreeterServer.
- We have the method “SayHello()” whose definition is the same as the one in “GreeterServer” Interface”
- “StubGreeterServer” has a variable “Reply”, which can be passed value from our test case.
Starting Stubserver and testing
package serverstub_test
import (
....
)
var gRPCClient *dao.GrpcClient
var stubServer helloworld.StubGreeterServer
func TestMain(m *testing.M) {
// ***** ***** gRPC Stub Server ***** *****
lisAddr, _ := net.Listen("tcp", "localhost:50051")
grpcServer := grpc.NewServer()
stubServer = helloworld.StubGreeterServer{}
helloworld.RegisterGreeterServer(grpcServer, &stubServer)
go grpcServer.Serve(lisAddr)
// ***** ***** gRPC Client ***** *****
mainCtx, cancel := context.WithCancel(context.Background()) // Main Context
defer cancel()
gRPCClient = dao.NewGrpcClient(mainCtx) // Initialize the gRPC client
// Run all Test Cases
code := m.Run()
os.Exit(code)
}
func Test_Client(t *testing.T) {
// Separate Context
requestCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// Initializing the "Reply" variable with deesited response.
stubServer.Reply = &helloworld.HelloReply{Message: "Demo Message"}
helloReply, _ := gRPCClient.MyFunc(requestCtx, "Some Message")
fmt.Printf("gRPC Response %s", helloReply.Message)
}
TestMain has three things going on
- Starting the gRPC stub server in a separate go routine
- Initializing the gRPC client (refer: gRPC client stub for the code)
- Running the test suite
Each Test case just needs to pass what response it is expecting from the server and continue with testing.
Happy Testing !!