Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Go - E2E Testing with `main` function
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Yuki Ito
June 14, 2019
Technology
2k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Go - E2E Testing with `main` function
Yuki Ito
June 14, 2019
More Decks by Yuki Ito
See All by Yuki Ito
newmo の創業を支える Software Architecture と Platform Engineering
110y
5
3.6k
Modular Monolith Go Server with GraphQL Federation + gRPC
110y
1
1.1k
Modular Monolith + Go @ newmo
110y
1
1.3k
Go + GraphQL @ newmo
110y
3
990
Architect / Platform Team at KAUCHE
110y
1
710
Cloud Run + Observability / Reliability @ KAUCHE
110y
0
680
Cloud Run CI/CD + QA @ KAUCHE
110y
1
680
Microservices on Cloud Run @ KAUCHE
110y
0
320
KAUCHE Loves Go
110y
0
540
Other Decks in Technology
See All in Technology
ガバメント AI 源内を地方自治体は活用できるのか可能性と課題、期待について
takeda_h
1
340
取引先から届く 「セキュリティチェックシート」の読み解き方
kamadamakoto
0
120
Invisible to AI? Making TYPO3 Sites Quotable by AI Search Systems
wolfgangwagner
0
160
制約理論(ToC)入門 2026版
recruitengineers
PRO
6
2k
モダンフロントエンド 開発研修
recruitengineers
PRO
3
550
クラウドセキュリティ入門 ~安全なクラウド利用のための基礎知識~
lhazy
11
8.3k
MIRU 2026 チュートリアル
keisuke198619
0
860
TypeScript入門 2026
recruitengineers
PRO
2
490
바이브풀 라이프
arawn
0
100
ラジオの科学
frievea
0
280
toio・myCobotでフィジカルAIっぽいことを行うための検討(とりあえず調査) / フィジカルAI LT(IoTLTによる開催)
you
PRO
0
300
『三匹の子ぶた』から学ぶネットワークセキュリティの昔と今 / Network Security: Then and Now Through the Lens of The Three Little Pigs
nttcom
1
1.8k
Featured
See All Featured
Practical Orchestrator
shlominoach
191
12k
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
270
Writing Fast Ruby
sferik
630
63k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
30k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.8k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
201
75k
GitHub's CSS Performance
jonrohan
1033
470k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
460
It's Worth the Effort
3n
188
29k
Odyssey Design
rkendrick25
PRO
2
750
From π to Pie charts
rasagy
0
260
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
190
Transcript
1 E2E Testing with `main` Yuki ITO
Table of Contents !2 ɾExample Server ɾTesting without `main` ɾTesting
with `main`
Table of Contents !3 ɾExample Server ɾTesting without `main` ɾTesting
with `main`
Example Server !4 https://github.com/110y/go-e2e-example Repository
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
Table of Contents !13 ɾExample Server ɾTesting without `main` ɾTesting
with `main`
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 { // … } }() //…
Table of Contents !24 ɾExample Server ɾTesting without `main` ɾTesting
with `main`
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.