Simplest approach
Just start all of your microservices. Have a look at this working example for testing gRPC services. Here's how you might modify it to connect r1, r2, and r3:
- Start r3
- Create an r3 client
- Start r2, providing the r3 client to your r2 server implementation
- Create an r2 client
- Start r1, providing the r2 client to your r1 server implementation
- In your test, create an r1 client and test its functionality
Untested example:
const bufSize = 1024 * 1024
var l1, l2, l3 *bufconn.Listener
func init() {
l3 = bufconn.Listen(bufSize)
s3 := grpc.NewServer()
pb.RegisterR3Server(s3, &r3server{})
go func() {
if err := s3.Serve(l3); err != nil {
log.Fatal(err)
}
}()
ctx := context.Background()
conn3, err := grpc.DialContext(
ctx, "bufnet",
grpc.WithContextDialer(func(_ interface{}, _ string) (interface{}, error) {
return l3.Dial()
}),
grpc.WithInsecure(),
)
if err != nil {
log.Fatal(err)
}
c3 := pb.NewR3Client(conn3)
l2 = bufconn.Listen(bufSize)
s2 := grpc.NewServer()
pb.RegisterR2Server(s2, &r2server{R3Client: c3})
go func() {
defer conn3.Close()
if err := s2.Serve(l2); err != nil {
log.Fatal(err)
}
}()
conn2, err := grpc.DialContext(
ctx, "bufnet",
grpc.WithContextDialer(func(_ interface{}, _ string) (interface{}, error) {
return l2.Dial()
}),
grpc.WithInsecure(),
)
if err != nil {
log.Fatal(err)
}
c2 := pb.NewR2Client(conn2)
l1 = bufconn.Listen(bufSize)
s1 := grpc.NewServer()
pb.RegisterR1Server(s1, &r1server{R2Client: c2})
go func() {
defer conn2.Close()
if err := s1.Serve(l1); err != nil {
log.Fatal(err)
}
}()
}
func bufDialer(ctx context.Context, address string) (net.Conn, error) {
return l1.Dial()
}
func TestSayHello(t *testing.T) {
ctx := context.Background()
conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure())
if err != nil {
t.Fatal(err)
}
defer conn.Close()
client := pb.NewR1Client(conn)
resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: "test"})
if err != nil {
t.Fatal(err)
}
if resp.GetMessage() != "Hello test" {
t.Fatal("hello reply must be 'Hello test'")
}
}
A more integration test-y approach
Use Testcontainers-go, and start each microservice in sequence on the same Docker network.