I'd like to test a sample Twirp RPC service. The sample service is from Twirp official website.
Protobuf definition is as follows:
syntax = "proto3";
package helloservice;
option go_package = "helloservice";
service HelloWorld {
rpc Hello(HelloReq) returns (HelloResp);
}
message HelloReq {
string subject = 1;
}
message HelloResp {
string text = 1;
}
Server implementation:
type HelloWorldServer struct{}
func (s *HelloWorldServer) Hello(ctx context.Context, req *pb.HelloReq) (*pb.HelloResp, error) {
return &pb.HelloResp{Text: "Hello " + req.Subject}, nil
}
I tried the solution suggested here, but there's some confusion in ClientConn vs HTTPClient. So far, I have this
var Once sync.Once
const bufSize = 1024 * 1024
var listener *bufconn.Listener
func InitTestServer(t *testing.T) *gomock.Controller {
Once.Do(func(){
listener = bufconn.Listen(bufSize)
server := &server.HelloWorldServer{}
twirpHandler := pb.NewHelloWorldServer(server, nil)
mux := http.NewServeMux()
mux.Handle(twirpHandler.PathPrefix(), twirpHandler)
httpServer := http.Server{
Handler: mux,
}
go func() {
if err := httpServer.Serve(listener); err != nil {
if err != http.ErrServerClosed {
log.Fatalln("Failed to start http listener", "error", err)
}
}
}()
})
ctrl := gomock.NewController(t)
return ctrl
}
func bufDialer(context.Context, string) (net.Conn, error) {
return listener.Dial()
}
func TestCreate(t *testing.T) {
//ctrl := InitTestServer(t)
InitTestServer(t)
ctx := context.Background()
conn, err := grpc.DialContext(ctx, "", grpc.WithInsecure(), grpc.WithContextDialer(bufDialer))
// conn is of type *ClientConn
if err != nil {
t.Fatalf("Failed to dial bufnet: %v", err)
}
defer conn.Close()
//NewHelloWorldJSONClient accepts only HTTPClient
client := pb.NewHelloWorldJSONClient(conn)
response, err := client.Hello(ctx, &pb.HelloReq{
Subject: "sample",
})
t.Log(response, err)
}
Any way to convert one into the other, or any other way to test out a Twirp RPC?
You can use net/http/httptest
to accomplish a test of this kind.
pb.NewHelloWorldServer
and giving it the server implementation struct.httptest.NewServer
using the handler..URL
in pb.NewHelloWorldJSONClient
(or protobuf, or both).As a quick example:
package main
import (
"context"
"net/http"
"net/http/httptest"
"testing"
pb "github.com/3ventic/twirphelloworld/rpc"
)
// InitTestServer initializes a test server for HelloWorld and returns its address
func InitTestServer() string {
handler := pb.NewHelloWorldServer(&HelloWorldServer{})
server := httptest.NewServer(handler)
return server.URL
}
func TestHello(t *testing.T) {
url := InitTestServer()
clients := map[string]pb.HelloWorld{
"json": pb.NewHelloWorldJSONClient(url, http.DefaultClient),
"pb": pb.NewHelloWorldProtobufClient(url, http.DefaultClient),
}
for typ, client := range clients {
t.Run(typ, func(t *testing.T) {
ctx := context.Background()
result, err := client.Hello(ctx, &pb.HelloReq{
Subject: "test",
})
if err != nil {
t.Error(err)
t.FailNow()
}
if result.Text != "Hello test" {
t.Errorf("result didn't match 'Hello test', was '%s'", result.Text)
}
})
}
}