2017-02-08 5 views
4

Ich möchte einen in Go geschriebenen gRPC-Dienst testen. Das Beispiel, das ich verwende, ist das Hello World Server-Beispiel von .Testen eines gRPC-Dienstes

Die protobuf Definition lautet wie folgt:

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; 
} 

Und die Art in der greeter_server Haupt ist:

// 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 
} 

Ich habe Beispiele gesucht, aber ich konnte nicht, wie man finden implementieren Sie Tests für einen gRPC-Dienst in Go.

+0

als Nebenbemerkung: beachten Sie die Standardeinstellung 4MiB –

Antwort

7

Wenn Sie überprüfen wollen, dass Die Implementierung des gRPC-Dienstes erfüllt Ihre Erwartungen. Dann können Sie einfach Standard-Komponententests schreiben und das Netzwerk vollständig ignorieren.

Zum Beispiel macht 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) 
     } 
    } 
} 

Ich habe vielleicht die Proto-Syntax etwas tut es aus dem Speicher durcheinander, aber das ist die Idee.

+0

Das ist der Weg, danke! – joscas

3

Ich kam mit der folgenden Implementierung, die möglicherweise nicht der beste Weg ist, es zu tun. Vor allem mit der TestMain Funktion drehen Sie den Server mit einem goroutine wie folgt aus:

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()) 
} 

und dann Umsetzung der Client in den Rest der Tests:

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

BTW: als neuer Beitrag kann ich Kommentare nicht hinzufügen. Also füge ich hier eine neue Antwort hinzu.

Ich kann bestätigen, dass der @Omar-Ansatz zum Testen eines nicht streaming gRPC-Dienstes durch Testen über die Schnittstelle ohne einen laufenden Dienst funktioniert.

Dieser Ansatz funktioniert jedoch nicht für Streams. Da gRPC bidirektionale Streams unterstützt, muss der Dienst über die Netzwerkschicht gestartet und mit ihm verbunden werden, um Tests für Streams durchführen zu können.

Der Ansatz, den @joscas übernimmt, funktioniert für gRPC-Streams (obwohl der Helloworld-Beispielcode keine Streams verwendet) mit einer Goroutine, um den Dienst zu starten. Ich habe jedoch festgestellt, dass unter Mac OS X 10.11.6 der Port, der vom Dienst verwendet wird, nicht konsistent freigegeben wird, wenn er von einer Goroutine aus aufgerufen wird (der Dienst blockiert die Goroutine und wird möglicherweise nicht sauber beendet). Indem ein separater Prozess für den Dienst gestartet wird, der "exec.Command" verwendet und vor dem Beenden beendet wird, wird der Port konsistent freigegeben.

ich hochgeladen eine Arbeitstestdatei für einen gRPC Dienststrom zu GitHub mit: https://github.com/mmcc007/go/blob/master/examples/route_guide/server/server_test.go

Sie können die Tests siehe travis laufen: https://travis-ci.org/mmcc007/go

Bitte lassen Sie mich wissen, ob irgendwelche Vorschläge, wie Tests zu verbessern für gRPC-Dienste.

1

Hier ist möglicherweise eine einfachere Möglichkeit, nur einen Streaming-Dienst zu testen. Entschuldigung, wenn es irgendwelche Tippfehler gibt, da ich das von irgendeinem laufenden Code anpasse.

Gegeben die folgende Definition.

rpc ListSites(Filter) returns(stream sites) 

Mit dem folgenden serverseitigen Code.

// 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 
} 

Alles was Sie jetzt tun müssen, ist mock die pb.SitesService_ListSitesServer in Ihren Tests Datei.

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 
} 

Diese reagiert auf das .send Ereignis und zeichnet die gesendeten Objekte in .Results, die Sie dann in Ihren assert-Anweisungen verwenden können.

Schließlich rufen Sie den Server-Code mit der mocked-Implementierung von 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") 
} 

Nein, es nicht den gesamten Stapel testen, aber es ermöglicht es Ihnen, zu Vernunft ohne lästiges Ihre Server-Side-Code überprüfen der Führung eines vollständigen gRPC Service bis entweder für echte oder in Mock Form.