2017-02-08 8 views
4

Goで書かれたgRPCサービスをテストしたいと思います。私が使用している例は、のHello Worldサーバーの例です。gRPCサービスのテスト

次のようにいるProtobuf定義は次のとおりです。

syntax = "proto3"; 

package helloworld; 

// The greeting service definition. 
service Greeter { 
    // Sends a greeting 
    rpc SayHello (HelloRequest) returns (HelloReply) {} 
} 

// The request message containing the user's name. 
message HelloRequest { 
    string name = 1; 
} 

// The response message containing the greetings 
message HelloReply { 
    string message = 1; 
} 

そしてメインgreeter_serverでタイプがある:

// server is used to implement helloworld.GreeterServer. 
type server struct{} 

// SayHello implements helloworld.GreeterServer 
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { 
    return &pb.HelloReply{Message: "Hello " + in.Name}, nil 
} 

私は例のために見てきたが、私はどのように上の任意のを見つけることができませんでしたGoでgRPCサービスのテストを実装する

+0

サイドノートとして:デフォルトの4MiBの制限を心配 –

答えて

7

主にそのようなゴルーチンを使用してサーバをスピンアップするTestMain機能を使用してgRPCサービスの実装はあなたが期待していることを行い、標準ユニットテストを書いてネットワーキングを完全に無視することができます。例えば

greeter_server_test.goを行います

func HelloTest(t *testing.T) { 
    s := server{} 

    // set up test cases 
    tests := []struct{ 
     name string 
     want string 
    } { 
     { 
      name: "world", 
      want: "Hello world", 
     }, 
     { 
      name: "123", 
      want: "Hello 123", 
     }, 
    } 

    for _, tt := range tests { 
     req := &pb.HelloRequest{Name: tt.name} 
     resp, err := s.SayHello(context.Background(), req) 
     if err != nil { 
      t.Errorf("HelloTest(%v) got unexpected error") 
     } 
     if resp.Message != tt.want { 
      t.Errorf("HelloText(%v)=%v, wanted %v", tt.name, resp.Message, tt.want) 
     } 
    } 
} 

私はビットがメモリからそれをやってプロト構文を台無しにしましたかもしれないが、それはアイデアです。

+0

それはありがとう、ありがとう! – joscas

3

私はそれを実行する最善の方法ではないかもしれない次の実装を考え出しました。

const (
    port = ":50051" 
) 

func Server() { 
    lis, err := net.Listen("tcp", port) 
    if err != nil { 
     log.Fatalf("failed to listen: %v", err) 
    } 
    s := grpc.NewServer() 
    pb.RegisterGreeterServer(s, &server{}) 
    if err := s.Serve(lis); err != nil { 
     log.Fatalf("failed to serve: %v", err) 
    } 
} 
func TestMain(m *testing.M) { 
    go Server() 
    os.Exit(m.Run()) 
} 

、その後、テストの残りの部分でクライアントを実装します:あなたはそれを確認したい場合は

func TestMessages(t *testing.T) { 

    // Set up a connection to the Server. 
    const address = "localhost:50051" 
    conn, err := grpc.Dial(address, grpc.WithInsecure()) 
    if err != nil { 
     t.Fatalf("did not connect: %v", err) 
    } 
    defer conn.Close() 
    c := pb.NewGreeterClient(conn) 

    // Test SayHello 
    t.Run("SayHello", func(t *testing.T) { 
     name := "world" 
     r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name}) 
     if err != nil { 
      t.Fatalf("could not greet: %v", err) 
     } 
     t.Logf("Greeting: %s", r.Message) 
     if r.Message != "Hello "+name { 
      t.Error("Expected 'Hello world', got ", r.Message) 
     } 

    }) 
} 
1

私はコメントに追加することはできません。だから私はここに新しい答えを加えています。

@Omarアプローチは、実行中のサービスなしでインターフェイス経由でテストすることで、非ストリーミングgRPCサービスをテストするために機能することが確認できます。

ただし、この方法はストリームでは機能しません。 gRPCは双方向ストリームをサポートしているので、ネットワーク層を介してサービスを起動してストリームに接続する必要があります。

@joscasがgRPCストリーム(helloworldサンプルコードではストリームを使用していなくても)に働くアプローチは、サービスを開始するためにgoroutineを使用します。しかし、Mac OS X 10.11.6では、goroutineから呼び出されたときにサービスで使用されるポートを一貫してリリースしないことに気付きました(私が理解しているように、このサービスはゴルーチンをブロックし、きれいに終了しません)。終了する前に 'exec.Command'を使用してサービスを実行する別のプロセスを起動し、終了する前に強制終了すると、ポートは一貫して解放されます。

私はgithubのにストリームを使用してgRPCサービスのための作業テストファイルをアップロード:https://github.com/mmcc007/go/blob/master/examples/route_guide/server/server_test.go

あなたはトラヴィス上で実行中のテストを見ることができます。https://travis-ci.org/mmcc007/go

を私がテストを改善する方法についてもしあれば提案をお聞かせくださいgRPCサービスのために。

1

これはおそらく、ストリーミングサービスをテストする簡単な方法です。私はいくつかの実行中のコードからこれを適応させているので、誤字がある場合はお詫び申し上げます。

以下の定義を与えます。

rpc ListSites(Filter) returns(stream sites) 

次のサーバー側コード。

// ListSites ... 
func (s *SitesService) ListSites(filter *pb.SiteFilter, stream pb.SitesService_ListSitesServer) error { 
    for _, site := range s.sites { 
     if err := stream.Send(site); err != nil { 
      return err 
     } 
    } 
    return nil 
} 

今あなたがしなければならないすべてはあなたのテストファイル内のモックpb.SitesService_ListSitesServerです。

type mockSiteService_ListSitesServer struct { 
    grpc.ServerStream 
    Results []*pb.Site 
} 

func (_m *mockSiteService_ListSitesServer) Send(site *pb.Site) error { 
    _m.Results = append(_m.Results, site) 
    return nil 
} 

これは.sendイベントに応答して、あなたが、あなたのアサート文で使用できる.Resultsで送信されたオブジェクトを記録します。

最後に、pb.SitesService_ListSitesServerの模擬非実装でサーバーコードを呼び出します。

func TestListSites(t *testing.T) { 
    s := SiteService.NewSiteService() 
    filter := &pb.SiteFilter{} 

    mock := &mockSiteService_ListSitesServer{} 
    s.ListSites(filter, mock) 

    assert.Equal(t, 1, len(mock.Results), "Sites expected to contain 1 item") 
} 

ありません、それはスタック全体をテストするものではありませんが、それは本当またはモック形態のいずれかのフルgRPCサービスを実行しているの手間をかけずに、サーバー側のコードをチェックする正気にあなたを認めていません。