Share
Embed
Go - E2E Testing with `main` function
Yuki Ito
June 14, 2019
Transcript
62 slides
-
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 -
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 -
Example Server !7 service Server { rpc Echo(EchoRequest) returns (EchoResponse)
{} } message EchoRequest { string echo = 1; } message EchoResponse { string echo = 1; } server/pb/server.proto -
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 -
Example Server !9 func (s *server) Echo(…) … { return
&pb.EchoResponse{ Echo: req.Echo, }, nil } server/server.go -
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 -
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 { // … } }() // … } -
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 -
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 -
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 -
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 -
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) } } -
Testing without `main` !18 $ go test ./server === RUN
TestEcho --- PASS: TestEcho (0.00s) PASS ok github.com/110y/go-e2e-example/server 0.022s -
Testing without `main` !19 func (s *server) Echo(…) … {
return &pb.EchoResponse{ Echo: req.Echo, }, nil } server/server.go Coverage -
Testing without `main` !20 func (s *server) Echo(…) … {
return &pb.EchoResponse{ Echo: req.Echo, }, nil } server/server.go Coverage However… -
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 { // … } }() // … } -
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 { // … } }() // … } -
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 { // … } }() //… -
Testing with `main` !25 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch -
Testing with `main` !26 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch -
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 -
Testing with `main` !28 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶉ m.Run() Launch ᶈ Connect -
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 } // ... }()) } -
Testing with `main` !30 Makefile .PHONY: test-server test-server: @go test
. \ -race \ -count=1 \ -covermode=atomic \ -coverprofile=coverage.out \ -run='^TestRunMain$$' -
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 -
Testing with `main` !32 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch -
Testing with `main` !33 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch -
Testing with `main` !34 main_test.go func TestRunMain(t *testing.T) { //
… go main() // … } -
Testing with `main` !35 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch -
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 { // … } }() // … } -
Testing with `main` !37 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch -
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) //… } -
Testing with `main` !39 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch -
Testing with `main` !40 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch -
Testing with `main` !41 e2e/e2e_test.go func TestMain(m *testing.M) { os.Exit(func()
(status int) { // … return m.Run() }()) } -
Testing with `main` !42 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch -
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 -
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) } } -
Testing with `main` !45 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch -
Testing with `main` !46 $ go test ./e2e === RUN
TestEcho --- PASS: TestEcho (0.00s) PASS ok github.com/110y/go-e2e-example/e2e 3.070s -
Testing with `main` !47 func (s *server) Echo(…) … {
return &pb.EchoResponse{ Echo: req.Echo, }, nil } server/server.go Coverage -
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 { // … } }() // … } -
Testing with `main` !49 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch -
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 -
Testing with `main` !51 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch -
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 } -
Testing with `main` !53 Termination main_test.go func TestRunMain(t *testing.T) {
// … go main() // … } -
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()` -
Testing with `main` !55 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch -
Testing with `main` !56 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶉ m.Run() Launch ᶈ Connect -
Testing with `main` !57 e2e/e2e_test.go func TestMain(m *testing.M) { os.Exit(func()
(status int) { // … return m.Run() }()) } Termination -
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 -
Testing with `main` !59 ᶃ go test ./e2e ᶆ go
test . ᶇ go main() ᶄ TestMain ᶊ TestEcho Start ᶅ exec.Command Request ᶈ Connect ᶉ m.Run() Launch ᶋ Dial -
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()` -
Conclusion !61 Testing with `main` gives us …. No redundant
setup Covering `main` code -
Future Work !62 Make termination more easier ɾGet test server
pid . from socket (like `lsof -i …`)? . using pgid? ɾSend `SIGTERM` to the process.