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
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
700
Cloud Run + Observability / Reliability @ KAUCHE
110y
0
670
Cloud Run CI/CD + QA @ KAUCHE
110y
1
670
Microservices on Cloud Run @ KAUCHE
110y
0
310
KAUCHE Loves Go
110y
0
540
Other Decks in Technology
See All in Technology
非定型なドキュメントを効率よくリファクタする 〜えぇ!?仕様書27本の移行が1日で終わったって!?〜
subroh0508
2
400
ローカルLLMとLINE Botの組み合わせ その3 / LINE DC Generative AI Meetup #8
you
PRO
0
130
SRE Next 2026 何でも屋からの脱却
bto
0
710
“それは自分の仕事じゃない"を越えて行け
yuukiyo
0
310
Road to SRE NEXTの今までとこれから
hiroyaonoe
0
320
GuardrailからGovernanceへ~AIエージェント運用の次の課題~
sbspsy
2
280
ソニー銀行におけるビジネスアジリティ向上のためのクラウドシフト戦略
srenext
0
240
公式ドキュメントの歩き方etc
coco_se
0
110
AI時代の開発生産性は、個人技からチーム設計へ
moongift
PRO
3
1.2k
インフラと開発の垣根を超えていき!〜元AWSインフラエンジニアがAWS開発で奮闘している話〜
hatahata021
3
180
技術イベント終了後、運営の 事後タスクは丁寧に (心がけています)/ #tamagawadev
nishiuma
1
100
壊して学ぶAWS CDK: そのcdk deployで消えるもの、残るもの
k_adachi_01
0
110
Featured
See All Featured
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
170
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.6k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
Paper Plane
katiecoart
PRO
2
52k
The World Runs on Bad Software
bkeepers
PRO
72
12k
Navigating Team Friction
lara
192
16k
GitHub's CSS Performance
jonrohan
1033
470k
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
460
From π to Pie charts
rasagy
0
230
Docker and Python
trallard
47
3.9k
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.