I created a simple app or back-end in Go to calculate weights. I built this app to demonstrate gRPC along with gRPC Gateway and its unit tests. gRPC Gateway is cool. You code once for gRPC services and gRPC Gateway translates those services to HTTP services for standard RESTful JSON API. There is no need to code the HTTP routes, JSON validation, and unit tests because the gRPC Gateway already covers them. It saves you time and effort. gRPC is fast and bandwidth-efficient. Feel free to read my previous article about gRPC.
The app is simple. It covers CRUD to demonstrate REST API and a simple calculation to calculate weights. I think I don't need to put its details here. Let's talk about the gRPC stuff only.
Protocol Buffers
I wrote the protocol buffers in proto3. Writing protocol buffers is the first thing we need to do for working with gRPC.
You can notice that there are some differences with ordinary protocol buffers. We have an option from google.api.http. We specified the routes along with their HTTP methods and parameters to that option. {date} means WeightParams.date. * means all fields of WeightParams. We can use google.protobuf.Empty if there is no request and response.
Buf
Now, it's time to compile the protocol buffers. I used Buf instead of protoc. Buf is a new way of working with protocol buffers. Follow my link and install Buf, https://docs.buf.build/installation/.
Put those YAML files into the root of your proto files or along with them. We put buf.build/googleapis/googleapis as the dependency. It will generate three Go files. One of them is from the grpc-gateway.
Go to the folder that has those YAML files and type buf generate on the terminal. You can put some parameters on Buf if you want to compile proto files from another folder such as the root of the project. Eg; buf generate --template tools/grpc/buf.gen.yaml --output tools/grpc/ tools/grpc.
gRPC Services
Now, it's time to code the gRPC services along with their unit tests.
This article will be too long if I explain all methods. Let me explain this method only. You can't return nil for *emptypb.Empty, use &emptypb.Empty{} instead. The date is a string in this app because the time is unnecessary.
func(t*weightGRPCTest)TestCreateWeight(){t.Run("success",func(){t.weightRepository.On("Create",data.Weights[0]).Return(nil).Once()_,err:=t.client.CreateWeight(context.Background(),&pb.WeightParams{Date:data.Weights[0].Date().Format("2006-01-02"),Max:data.Weights[0].Max(),Min:data.Weights[0].Min(),})t.NoError(err)})t.Run("failed",func(){t.weightRepository.On("Create",data.Weights[0]).Return(errors.New("failed to create a weight")).Once()_,err:=t.client.CreateWeight(context.Background(),&pb.WeightParams{Date:data.Weights[0].Date().Format("2006-01-02"),Max:data.Weights[0].Max(),Min:data.Weights[0].Min(),})t.Error(err)})t.Run("failed to create a weight",func(){_,err:=t.client.CreateWeight(context.Background(),&pb.WeightParams{date:2023-01-01",
})
t.Error(err)
})
t.Run("failedtoparsethedate",func(){_,err:=t.client.CreateWeight(context.Background(),&pb.WeightParams{})t.Error(err)})}
This was how I tested the CreateWeight method. We mocked the weightRepository in some sub-tests so we can test the CreateWeight method for any condition. We don't need to assert the *emptypb.Empty.
I used the suite package from Testify to test this gRPC service. The initial setup to test this is to create a dial function, dialer(), then create a connection for the gRPC service client. 1024 * 1024 is the capacity of the data in a pipe, it is a ring buffer of fixed capacity.
Conclusion
gRPC Gateway and Buf are cool. This is the best option if you want to provide services for both gRPC and HTTP standard REST APIs in a single codebase. Even this condition is not often. Because gRPC is widely used for communication between internal services. HTTP REST API is widely used for front-end or public. There is also a tool called GraphQL to provide APIs better than the HTTP REST API to the public.
Writing unit tests for gRPC is a little bit confusing at first. I hope this article can help. You can check the completed code, https://github.com/aristorinjuang/weight-go, which is 100% tested.