Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go - E2E Testing with `main` function

Yuki Ito
June 14, 2019

Go - E2E Testing with `main` function

Yuki Ito

June 14, 2019
Tweet

More Decks by Yuki Ito

Other Decks in Technology

Transcript

  1. Example Server !5 !"" e2e # !"" e2e_test.go # $""

    echo_test.go !"" server # !"" pb # # !"" server.pb.go # # $"" server.proto # $"" server.go !"" main.go $"" main_test.go packages
  2. Example Server !6 !"" e2e # !"" e2e_test.go # $""

    echo_test.go !"" server # !"" pb # # !"" server.pb.go # # $"" server.proto # $"" server.go !"" main.go $"" main_test.go packages
  3. Example Server !7 service Server { rpc Echo(EchoRequest) returns (EchoResponse)

    {} } message EchoRequest { string echo = 1; } message EchoResponse { string echo = 1; } server/pb/server.proto
  4. Example Server !8 !"" e2e # !"" e2e_test.go # $""

    echo_test.go !"" server # !"" pb # # !"" server.pb.go # # $"" server.proto # $"" server.go !"" main.go $"" main_test.go packages
  5. Example Server !9 func (s *server) Echo(…) … { return

    &pb.EchoResponse{ Echo: req.Echo, }, nil } server/server.go
  6. Example Server !10 !"" e2e # !"" e2e_test.go # $""

    echo_test.go !"" server # !"" pb # # !"" server.pb.go # # $"" server.proto # $"" server.go !"" main.go $"" main_test.go packages
  7. Example Server !11 main.go func main() { // … lis,

    err := net.Listen("tcp", addr) // … gs := grpc.NewServer() pb.RegisterServerServer(gs, &server.Server{}) go func() { if err := gs.Serve(lis); err != nil { // … } }() // … }
  8. Example Server !12 $ grpcurl \ > -plaintext \ >

    -d "$(jo -p echo='Welcome to mercari.go #8')" \ > localhost:9090 server.Server/Echo { "echo": "Welcome to mercari.go #8" } Request / Response
  9. Testing without `main` !14 !"" e2e # !"" e2e_test.go #

    $"" echo_test.go !"" server # !"" pb # # !"" server.pb.go # # $"" server.proto # $"" server.go !"" main.go $"" main_test.go packages
  10. Testing without `main` !15 func TestMain(m *testing.M) { os.Exit(func() (status

    int) { // … lis, err := net.Listen(“tcp", addr) // … gs := grpc.NewServer() pb.RegisterServerServer(gs, &Server{}) go func() { if err := gs.Serve(lis); err != nil { // … } }() // … }()) } server/server_test.go
  11. Testing without `main` !16 func TestMain(m *testing.M) { os.Exit(func() (status

    int) { // … conn, err := grpc.DialContext( context.Background(), fmt.Sprintf(":%s", port), grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(5*time.Second), ) // … grpcClient = pb.NewServerClient(conn) // … return m.Run() }()) } server/server_test.go
  12. Testing without `main` !17 server/server_test.go func TestEcho(t *testing.T) { //

    … req := &pb.EchoRequest{ Echo: "foo", } res, err := grpcClient.Echo(ctx, req) if err != nil { t.Fatalf("failed to request to the server: %s", err) } if res.Echo != req.Echo { t.Errorf("want %s, but got %s", req.Echo, res.Echo) } }
  13. Testing without `main` !18 $ go test ./server === RUN

    TestEcho --- PASS: TestEcho (0.00s) PASS ok github.com/110y/go-e2e-example/server 0.022s
  14. Testing without `main` !19 func (s *server) Echo(…) … {

    return &pb.EchoResponse{ Echo: req.Echo, }, nil } server/server.go Coverage
  15. Testing without `main` !20 func (s *server) Echo(…) … {

    return &pb.EchoResponse{ Echo: req.Echo, }, nil } server/server.go Coverage However…
  16. Testing without `main` !21 `main` is NOT covered main.go func

    main() { // … lis, err := net.Listen("tcp", addr) // … gs := grpc.NewServer() pb.RegisterServerServer(gs, &server.Server{}) go func() { if err := gs.Serve(lis); err != nil { // … } }() // … }
  17. Testing without `main` !22 Duplication between main.go & server_test.go func

    main() { // … lis, err := net.Listen("tcp", addr) // … gs := grpc.NewServer() pb.RegisterServerServer(gs, &server.Server{}) go func() { if err := gs.Serve(lis); err != nil { // … } }() // … }
  18. Testing without `main` !23 Duplication between main.go & server_test.go func

    TestMain(m *testing.M) { os.Exit(func() (status int) { // … lis, err := net.Listen(“tcp", addr) // … gs := grpc.NewServer() pb.RegisterServerServer(gs, &Server{}) go func() { if err := gs.Serve(lis); err != nil { // … } }() //…
  19. Testing with `main` !25 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch
  20. Testing with `main` !26 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch
  21. Testing with `main` !27 !"" e2e # !"" e2e_test.go #

    $"" echo_test.go !"" server # !"" pb # # !"" server.pb.go # # $"" server.proto # $"" server.go !"" main.go $"" main_test.go packages
  22. Testing with `main` !28 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶉ m.Run() Launch ᶈ Connect
  23. Testing with `main` !29 e2e/e2e_test.go func TestMain(m *testing.M) { os.Exit(func()

    (status int) { // … cmd := exec.CommandContext( ctx, “make", “test-server”, ) if err := cmd.Start(); err != nil { return 1 } // ... }()) }
  24. Testing with `main` !30 Makefile .PHONY: test-server test-server: @go test

    . \ -race \ -count=1 \ -covermode=atomic \ -coverprofile=coverage.out \ -run='^TestRunMain$$'
  25. Testing with `main` !31 !"" e2e # !"" e2e_test.go #

    $"" echo_test.go !"" server # !"" pb # # !"" server.pb.go # # $"" server.proto # $"" server.go !"" main.go $"" main_test.go packages
  26. Testing with `main` !32 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch
  27. Testing with `main` !33 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch
  28. Testing with `main` !35 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch
  29. Testing with `main` !36 main.go func main() { // …

    lis, err := net.Listen("tcp", addr) // … gs := grpc.NewServer() pb.RegisterServerServer(gs, &server.Server{}) go func() { if err := gs.Serve(lis); err != nil { // … } }() // … }
  30. Testing with `main` !37 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch
  31. Testing with `main` !38 e2e/e2e_test.go func TestMain(m *testing.M) { os.Exit(func()

    (status int) { // … if err := cmd.Start(); err != nil { return 1 } conn, err := grpc.DialContext( ctx, fmt.Sprintf(":%s", port), grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(5*time.Second), ) //… grpcClient = pb.NewServerClient(conn) //… }
  32. Testing with `main` !39 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch
  33. Testing with `main` !40 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch
  34. Testing with `main` !42 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch
  35. Testing with `main` !43 !"" e2e # !"" e2e_test.go #

    $"" echo_test.go !"" server # !"" pb # # !"" server.pb.go # # $"" server.proto # $"" server.go !"" main.go $"" main_test.go packages
  36. Testing with `main` !44 echo_test.go func TestEcho(t *testing.T) { //

    … req := &pb.EchoRequest{ Echo: "foo", } res, err := grpcClient.Echo(ctx, req) if err != nil { t.Fatalf("failed to request to the server: %s", err) } if res.Echo != req.Echo { t.Errorf("want %s, but got %s", req.Echo, res.Echo) } }
  37. Testing with `main` !45 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch
  38. Testing with `main` !46 $ go test ./e2e === RUN

    TestEcho --- PASS: TestEcho (0.00s) PASS ok github.com/110y/go-e2e-example/e2e 3.070s
  39. Testing with `main` !47 func (s *server) Echo(…) … {

    return &pb.EchoResponse{ Echo: req.Echo, }, nil } server/server.go Coverage
  40. Testing with `main` !48 Coverage main.go func main() { //

    … lis, err := net.Listen("tcp", addr) // … gs := grpc.NewServer() pb.RegisterServerServer(gs, &server.Server{}) go func() { if err := gs.Serve(lis); err != nil { // … } }() // … }
  41. Testing with `main` !49 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch
  42. Testing with `main` !50 ᶃ go test ./e2e ᶆ go

    test . .PHONY: test-server test-server: @go test . \ -race \ -count=1 \ -covermode=atomic \ -coverprofile=coverage.out \ -run='^TestRunMain$$' .PHONY: test test: go test -v -race -count=1 ./e2e
  43. Testing with `main` !51 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch
  44. Testing with `main` !52 func main() { // … lis,

    err := net.Listen("tcp", addr) // … gs := grpc.NewServer() pb.RegisterServerServer(gs, &server.Server{}) go func() { if err := gs.Serve(lis); err != nil { // … } }() // … } func (s *server) Echo(…) … { return &pb.EchoResponse{ Echo: req.Echo, }, nil }
  45. Testing with `main` !54 Termination main_test.go func TestRunMain(t *testing.T) {

    go main() lis, err := net.Listen("tcp", addr) // … conn, err := lis.Accept() // … } Blocking with `Accept()`
  46. Testing with `main` !55 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch
  47. Testing with `main` !56 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶉ m.Run() Launch ᶈ Connect
  48. Testing with `main` !57 e2e/e2e_test.go func TestMain(m *testing.M) { os.Exit(func()

    (status int) { // … return m.Run() }()) } Termination
  49. Testing with `main` !58 e2e/e2e_test.go func TestMain(m *testing.M) { os.Exit(func()

    (status int) { // … defer func() { // … termConn, err := net.Dial("tcp", addr) // … }() return m.Run() }()) } Termination
  50. Testing with `main` !59 ᶃ go test ./e2e ᶆ go

    test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch ᶋ Dial
  51. Testing with `main` !60 Termination main_test.go func TestRunMain(t *testing.T) {

    go main() lis, err := net.Listen("tcp", addr) // … conn, err := lis.Accept() // … } Blocking with `Accept()`
  52. Future Work !62 Make termination more easier ɾGet test server

    pid . from socket (like `lsof -i …`)? . using pgid? ɾSend `SIGTERM` to the process.