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.7k
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
330
KAUCHE Loves Go
110y
0
550
Other Decks in Technology
See All in Technology
SQL文一行も書けない人事がCortexもろもろを使って人事業務を楽にしてみる
ponponmikankan
1
240
Reactの設計論
uhyo
22
12k
AI 時代のスタートアップエコシステ厶から考究する技術的負債との向き合い方
m3m0r7
PRO
2
1.6k
The Agent Builder Loop from Daily Work to OSS
minorun365
PRO
3
190
GoCon2026 - Open Source, Open World
sanposhiho
4
4.3k
AIを活用するために決めた "やらないこと" - 価値に注目する / Not betting on AI
soudai
PRO
1
510
Sigmaユーザーのための有用リソース一挙公開 & Sigmaで使えるMCP #sigma_ucj /useful-resources-for-sigma-computing-users-and-mcps-with-sigma
shinyaa31
0
230
[2026-09-11]SREは誰のもの?運用エンジニアが始める 「SRE領域への越境」とチームの進化の軌跡 〜Road to NEXT CRE
tosite
0
220
アプリをもっと"iOSアプリっぽく"する小さな工夫 / Small Touches That Make Your App Feel More Like an iOS App
matsuji
1
860
Gitは怖い?共有ワークスペースから始めるSnowflakeチーム開発
coco_se
0
210
絵ではじめるKubernetesセキュリティ
aoi1
3
500
20260912_スクフェス三河
kgnkhkr
0
360
Featured
See All Featured
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
450
How to make the Groovebox
asonas
2
2.4k
How to Ace a Technical Interview
jacobian
281
24k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
Designing Experiences People Love
moore
143
24k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
880
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
850
Building Applications with DynamoDB
mza
96
7.2k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
2
430
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.4k
Game over? The fight for quality and originality in the time of robots
wayneb77
1
270
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
690
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.