Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Go - E2E Testing with `main` function
Search
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.8k
Modular Monolith Go Server with GraphQL Federation + gRPC
110y
1
1.2k
Modular Monolith + Go @ newmo
110y
1
1.3k
Go + GraphQL @ newmo
110y
3
1k
Architect / Platform Team at KAUCHE
110y
1
730
Cloud Run + Observability / Reliability @ KAUCHE
110y
0
690
Cloud Run CI/CD + QA @ KAUCHE
110y
1
690
Microservices on Cloud Run @ KAUCHE
110y
0
340
KAUCHE Loves Go
110y
0
550
Other Decks in Technology
See All in Technology
カンファレンスに参加した後の浮遊感とセルフケア
pauli
0
310
HolmesGPTで始めるSREエージェント入門!プラットフォームの障害調査はAIにお任せ 〜
leveragestech
PRO
0
150
Oracle Base Database Service 技術詳細
oracle4engineer
PRO
16
120k
BedrockとLambdaで作る リアルタイム進行型推理ゲーム
kawametho
0
190
SREでアラート疲れを 解決しよう!
kairim0
1
200
More Freedom on the Same Shared GPU Cluster: A Small Team’s Experience with vCluster
nttcom
0
130
購入ドメインでの課題と取り組み
ykagano
0
140
全社共通データ基盤をつくる。ソニーのDatabricks活用とデータガバナンス設計の裏側
sony
0
420
『GOエコノミー 』(相乗りサービス) におけるスペック駆動開発
mot_techtalk
1
310
Snowflake Horizon Catalog と Apache Iceberg で作る オープンなデータ基盤
kitagawaz
0
440
Futexes the good, the bad, the ugly
ennael
PRO
0
120
組み立てて楽しむ AWS Blocks 入門
kmiya84377
0
210
Featured
See All Featured
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
68
58k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
2
310
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
520
StorybookのUI Testing Handbookを読んだ
zakiyama
31
7k
How to train your dragon (web standard)
notwaldorf
96
6.8k
Docker and Python
trallard
47
4.2k
Heart Work Chapter 1 - Part 1
lfama
PRO
10
36k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Marketing to machines
jonoalderson
1
5.8k
[SF Ruby Conf 2025] Rails X
palkan
3
1.4k
Designing for humans not robots
tammielis
254
26k
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.