Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
© - BASE, Inc. X Cost-effective Go Unit Test thinking and practices Go Conference’ Summer in Fukuoka . . - @hgsgtk
Slide 2
Slide 2 text
© - BASE, Inc. Talk Structure Cost-effective Unit Test Go Unit Test Basic Real Practices in Go WHY BASIC PRACTICE
Slide 3
Slide 3 text
© - BASE, Inc. : @hgsgtk ⾃⼰紹介 東⼝ 和暉 ( Higashiguchi Kazuki ) Go, PHP, Python BASE BANK, Inc. (BASE, Inc.) / Dev Division / Tech Lead
Slide 4
Slide 4 text
© - BASE, Inc. Talk Structure Cost-effective Unit Test Go Unit Test Basic Real Practices in Go WHY BASIC PRACTICE
Slide 5
Slide 5 text
© - BASE, Inc. Cost-effective Unit Test
Slide 6
Slide 6 text
© - BASE, Inc. Unit Testとは • プログラムを構成する⽐較的⼩さな単位(ユニッ ト)が個々の機能を正しく果たしているかどうか を検証する • 実⾏⼿段として、⼿動によるテスト‧⾃動化テス トが存在する • このトークでは、⾃動化テストを取り上げる
Slide 7
Slide 7 text
© - BASE, Inc. WHY? なぜUnit Testを書くのか • ⽋陥混⼊の阻⽌、バグを防ぎたい? • テストによるドキュメンテーション? • 設計改善の指標?
Slide 8
Slide 8 text
© - BASE, Inc. このトークで定義する⽬的 コスト削減
Slide 9
Slide 9 text
© - BASE, Inc. コスト削減の指標 Unit Test ʹΑΔ ίετݮ Unit Test ͷ ࡞ɾҡ࣋ίετ VS
Slide 10
Slide 10 text
© - BASE, Inc. • ৽نςετ࡞ίετൃੜ • طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷͪίετൃੜ • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ ユニットテストの両⾯のコスト Unit Test ʹΑΔ ίετݮ Unit Test ͷ ࡞ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ • υΩϡϝϯςʔγϣϯίετݮ • σόοάίετݮ • ઃܭվળʹΑΔϝϯςφϯείετݮ
Slide 11
Slide 11 text
© - BASE, Inc. • ৽نςετ࡞ίετൃੜ • طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷͪίετൃੜ • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ コストへの集約 Unit Test ʹΑΔ ίετݮ Unit Test ͷ ࡞ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετݮ •ܽؕͷૣظൃݟʹΑΔ मਖ਼ίετݮ • υΩϡϝϯςʔγϣϯίετݮ • σόοάίετݮ • ઃܭվળʹΑΔϝϯςφϯείετݮ ⽋陥混⼊の阻⽌、バグを防ぎたい?
Slide 12
Slide 12 text
© - BASE, Inc. • ৽نςετ࡞ίετൃੜ • طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷͪίετൃੜ • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ Unit Test ʹΑΔ ίετݮ Unit Test ͷ ࡞ɾҡ࣋ίετ VS テストによるドキュメンテーション? • खಈϢχοτςετͷίετݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ •υΩϡϝϯςʔγϣϯ ίετݮ • σόοάίετݮ • ઃܭվળʹΑΔϝϯςφϯείετݮ コストへの集約
Slide 13
Slide 13 text
© - BASE, Inc. • ৽نςετ࡞ίετൃੜ • طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷͪίετൃੜ • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ Unit Test ʹΑΔ ίετݮ Unit Test ͷ ࡞ɾҡ࣋ίετ VS 設計改善の指標? • खಈϢχοτςετͷίετݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ • υΩϡϝϯςʔγϣϯίετݮ • σόοάίετݮ •ઃܭվળʹΑΔ ϝϯςφϯείετݮ コストへの集約
Slide 14
Slide 14 text
© - BASE, Inc. Cost effective Unit Test Unit Test ʹΑΔ ίετݮ Unit Test ͷ ࡞ɾҡ࣋ίετ >
Slide 15
Slide 15 text
© - BASE, Inc. See also https://speakerdeck.com/hgsgtk/practices-to-write-better-unit-test
Slide 16
Slide 16 text
© - BASE, Inc. Talk Structure Cost-effective Unit Test Go Unit Test Basic Real Practices in Go WHY BASIC PRACTICE
Slide 17
Slide 17 text
© - BASE, Inc. BASIC Go Unit Test Basic • 適切なエラーレポート • 適切なエラーハンドリング • テーブル駆動テスト‧サブテスト BASIC
Slide 18
Slide 18 text
© - BASE, Inc. BASIC Go Unit Test Basic • 適切なエラーレポート • 適切なエラーハンドリング • テーブル駆動テスト‧サブテスト BASIC
Slide 19
Slide 19 text
© - BASE, Inc. 例題:不適切なエラーレポート
Slide 20
Slide 20 text
© - BASE, Inc. 例題:不適切なエラーレポート === RUN TestInStatusListInAppropriateErrorReport --- FAIL: TestInStatusListInAppropriateErrorReport ( . s) simple_test.go: : unexpected value true simple_test.go: : unexpected value true FAIL FAIL github.com/hgsgtk/go-snippets/testing-codes/simple . s
Slide 21
Slide 21 text
© - BASE, Inc. 不適切なエラーレポートの弊害 . 「どこで何を検証して失敗したのか」が読みとり にくい . ⼊⼒‧期待値が結果から読み取れない . 問題が起きたか知るためにテストコード‧動作 コードを読む必要がある --- FAIL: TestInStatusListInAppropriateErrorReport ( . s) simple_test.go: : unexpected value true
Slide 22
Slide 22 text
© - BASE, Inc. 適切なエラーレポートとは Go FAQ “Why does Go not have assertions?“ Refs: https://golang.org/doc/faq#assertions “Proper error reporting means that errors are direct and to the point, saving the programmer from interpreting a large crash trace.” 直接的 かつ 適切
Slide 23
Slide 23 text
© - BASE, Inc. 例題を改善する
Slide 24
Slide 24 text
© - BASE, Inc. 例題を改善する 直接的かつ適切なエラーレポートを⾏う ex. “f(x) = y, want z”という⼀つの形式 f(x): 試みた結果と⼊⼒ y: 得られた結果 z: 期待値
Slide 25
Slide 25 text
© - BASE, Inc. 例題を改善する === RUN TestInStatusListProperErrorReport --- FAIL: TestInStatusListProperErrorReport ( . s) simple_test.go: : InStatusList(`unknown `) = true, want false simple_test.go: : InStatusList(`unknown `) = true, want false FAIL FAIL github.com/hgsgtk/go-snippets/testing-codes/simple
Slide 26
Slide 26 text
© - BASE, Inc. • ৽نςετ࡞ίετൃੜ •طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷͪίετൃੜ • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ 適切なエラーレポートの効果 Unit Test ʹΑΔ ίετݮ Unit Test ͷ ࡞ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ • υΩϡϝϯςʔγϣϯίετݮ • σόοάίετݮ • ઃܭվળʹΑΔϝϯςφϯείετݮ テスト失敗時の 原因調査コストの削減
Slide 27
Slide 27 text
© - BASE, Inc. BASIC Go Unit Test Basic • 適切なエラーレポート • 適切なエラーハンドリング • テーブル駆動テスト‧サブテスト BASIC
Slide 28
Slide 28 text
© - BASE, Inc. 適切なエラーハンドリングとは “Proper error handling means that servers continue to operate instead of crashing after a non-fatal error.” 致命的ではないエラーの場合はクラッ シュせずに処理を継続する Go FAQ “Why does Go not have assertions?“ Refs: https://golang.org/doc/faq#assertions
Slide 29
Slide 29 text
© - BASE, Inc. 適切なエラーハンドリング
Slide 30
Slide 30 text
© - BASE, Inc. 適切なエラーハンドリング ここで失敗すると後続の検証が不可なので致命的と して、testing.Fatalf()でハンドリング
Slide 31
Slide 31 text
© - BASE, Inc. 適切なエラーハンドリング 後続の処理可能なため t.Errorf() でハンドリングす る
Slide 32
Slide 32 text
© - BASE, Inc. • ৽نςετ࡞ίετൃੜ •طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷͪίετൃੜ • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ 適切なエラーハンドリングの効果 Unit Test ʹΑΔ ίετݮ Unit Test ͷ ࡞ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ • υΩϡϝϯςʔγϣϯίετݮ • σόοάίετݮ • ઃܭվળʹΑΔϝϯςφϯείετݮ 致命的なもののみをcrashさ せるため、継続可能な限り、 ⼀回の実⾏で多く検証結果を 得られる
Slide 33
Slide 33 text
© - BASE, Inc. BASIC Go Unit Test Basic • 適切なエラーレポート • 適切なエラーハンドリング • テーブル駆動テスト‧サブテスト BASIC
Slide 34
Slide 34 text
© - BASE, Inc. テーブル駆動テストとは “Each table entry is a complete test case with inputs and expected results, and sometimes with additional information such as a test name to make the test output easily readable.” ⼊⼒と期待値を含むテストエントリ Refs: https://github.com/golang/go/wiki/TableDrivenTests
Slide 35
Slide 35 text
© - BASE, Inc. テーブル駆動テスト‧サブテスト
Slide 36
Slide 36 text
© - BASE, Inc. テーブル駆動テスト‧サブテスト ⼊⼒と期待値を含むテストエントリ
Slide 37
Slide 37 text
© - BASE, Inc. テーブル駆動テスト‧サブテスト testing.Run()でサブテストとして実⾏ -run で特定ケースを実⾏できる go test ./... -v -run TestGetMsgSubTest/ divisible_by_
Slide 38
Slide 38 text
© - BASE, Inc. •৽نςετ࡞ίετൃੜ •طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷͪίετൃੜ • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ テーブル駆動テスト‧サブテストの効果 Unit Test ʹΑΔ ίετݮ Unit Test ͷ ࡞ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ • υΩϡϝϯςʔγϣϯίετݮ •σόοάίετݮ • ઃܭվળʹΑΔϝϯςφϯείετݮ サブテストによる特定テストケースの実⾏
Slide 39
Slide 39 text
© - BASE, Inc. •৽نςετ࡞ίετൃੜ •طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷͪίετൃੜ • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ テーブル駆動テスト‧サブテストの効果 Unit Test ʹΑΔ ίετݮ Unit Test ͷ ࡞ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ • υΩϡϝϯςʔγϣϯίετݮ •σόοάίετݮ • ઃܭվળʹΑΔϝϯςφϯείετݮ テストパターンと検証ロジッ クの分離の効果 同じ検証ロジックを何度も書 かずに済む
Slide 40
Slide 40 text
© - BASE, Inc. •৽نςετ࡞ίετൃੜ •طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷͪίετൃੜ • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ テーブル駆動テスト‧サブテストの効果 Unit Test ʹΑΔ ίετݮ Unit Test ͷ ࡞ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ • υΩϡϝϯςʔγϣϯίετݮ •σόοάίετݮ • ઃܭվળʹΑΔϝϯςφϯείετݮ エントリ追加で テストのバリエーションを修 正できる
Slide 41
Slide 41 text
© - BASE, Inc. Talk Structure Cost-effective Unit Test Go Unit Test Basic Real Practices in Go WHY BASIC PRACTICE
Slide 42
Slide 42 text
© - BASE, Inc. PRACTICE Real Practices in Go PRACTICE BASIC WHY カスタムアサーション テストダブルを使うテスト RDBMSを使う処理のテスト
Slide 43
Slide 43 text
© - BASE, Inc. PRACTICE Real Practices in Go PRACTICE BASIC WHY カスタムアサーション テストダブルを使うテスト RDBMSを使う処理のテスト
Slide 44
Slide 44 text
© - BASE, Inc. 起きがちな課題:Copy & Paste
Slide 45
Slide 45 text
© - BASE, Inc. 起きがちな課題:Copy & Paste
Slide 46
Slide 46 text
© - BASE, Inc. テストコードが増えてくると • HTTP Handlerなど関⼼事が領域が近い 処理のテストコードは、同じ関⼼領域を 類似した検証ロジックでテストする • コピペが⽣まれると往々にして「適切な エラーレポート」が忘れられる • ⇒ テストの維持コストの増加
Slide 47
Slide 47 text
© - BASE, Inc. 共通処理を⾏うテストヘルパーの選択肢 • 同様の関⼼‧ユースケースがある処理 は testutil パッケージなどにして共通化 する • ⇒ 新規テストコストの削減
Slide 48
Slide 48 text
© - BASE, Inc. 実例:HTTP Handlerのアサーションを共通処理にする • t: *testing.T を求め、t.Helper()をcall • testTarget: 「どのHandlerを施⾏するか」を設定 • gotRes: 試⾏結果の *http.Response • wantCode, bodyFile: 期待値
Slide 49
Slide 49 text
© - BASE, Inc. 実例:HTTP Handlerのアサーションを共通処理にする AssertResponse()をcall JSONのresponse bodyの期待値は別ファイルで配置
Slide 50
Slide 50 text
© - BASE, Inc. 実例:HTTP Handlerのアサーションを共通処理にする • testdata ディレクトリを作成 • ⇒パッケージとしてみなされない、テスト時のみ必要なファ イルであることが明⽰的になる • *.json.golden 拡張⼦として配置 • ⇒ 標準パッケージでも使われている⼿法 • ⇒テストのドキュメントとしての価値の向上
Slide 51
Slide 51 text
© - BASE, Inc. 実例:HTTP Handlerのアサーションを共通処理にする
Slide 52
Slide 52 text
© - BASE, Inc. 実例:HTTP Handlerのアサーションを共通処理にする
Slide 53
Slide 53 text
© - BASE, Inc. 実例:HTTP Handlerのアサーションを共通処理にする
Slide 54
Slide 54 text
© - BASE, Inc. 実例:HTTP Handlerのアサーションを共通処理にする 別ファイル定義したJSONファイルと⽐較 ⽐較には google/go-cmp を利⽤
Slide 55
Slide 55 text
© - BASE, Inc. PRACTICE Real Practices in Go PRACTICE BASIC WHY カスタムアサーション テストダブルを使うテスト RDBMSを使う処理のテスト
Slide 56
Slide 56 text
© - BASE, Inc. テストダブル Test Double: テスト固有の同等物 Mock: テスト対象に適切に使⽤されているか検証 する Refs ॻ੶ʰxUnit Test Patterns: Refactoring Test Codeʱ / Chapter 23. Test Double Patterns
Slide 57
Slide 57 text
© - BASE, Inc. ※補⾜:このトーク内での “Mock” Refs ॻ੶ʰςετۦಈ։ൃʱ / C ϢχοτςετपลͷࣝͷཧͱTDD֦ுͷࢼΈ • xUTPの語彙整理により、「広義のMock Object」‧「狭義のMock Object」を分けて議 論できるようになった • このトークでの “Mock” もTest Doubleの範囲を ざっくりと含める「広義のMock Object」とする
Slide 58
Slide 58 text
© - BASE, Inc. テストダブルを使うテスト 次のような属性を持つものに依存する場合に、テス ト時にテストダブルに置き換える事がある • 結果が不定 • ex: UUID • システム外コンポーネントを利⽤する処理 • ex: API, RDBMS, AWS etc • レイヤ構造をまたぐ依存 • ex: Service->Repository
Slide 59
Slide 59 text
© - BASE, Inc. テストダブル作成の選択肢としてのgomock • “GoMock is a mocking framework for the Go programming language.” • https://github.com/golang/mock
Slide 60
Slide 60 text
© - BASE, Inc. 例: gomockを利⽤するテスト例
Slide 61
Slide 61 text
© - BASE, Inc. 例: gomockを利⽤するテスト例
Slide 62
Slide 62 text
© - BASE, Inc. 例: gomockを利⽤するテスト例 gomockで依存関係のあるInterfaceを実装したモックを作成 Times()をcallして、呼び出される期待を設定する => 狭義のMock
Slide 63
Slide 63 text
© - BASE, Inc. この例の問題点 • すべての依存関係に呼び出しの期待を設 定している • 外の振る舞いを変えない内部構造の変更 に敏感に反応する • ⇒ リファクタリングの障害になる
Slide 64
Slide 64 text
© - BASE, Inc. 的確なエクスペクテーション Refs ॻ੶ʰ࣮ફςετۦಈ։ൃʱ / ୈ20ষ ςετͷΛௌ͘ “意図を明確にするためには、スタブとエクスペク テーション‧アサーションをきっちり区別するとよ い。”
Slide 65
Slide 65 text
© - BASE, Inc. 区別するための指標:コマンド/クエリ Refs ॻ੶ʰ࣮ફςετۦಈ։ൃʱ / ୈ20ষ ςετͷΛௌ͘ ΫΤϦʹΞϩʔΞϯεɺίϚϯυʹΤΫεϖΫςʔγϣϯ • コマンドとは、 副作⽤を伴うことのある呼び出し • オブジェクトの外の世界を変化させる • ex. DBのレコード保存‧状態変化 • 複数回実⾏した場合、システム状態は違ったもの になる => エクスペクテーション
Slide 66
Slide 66 text
© - BASE, Inc. 区別するための指標:コマンド/クエリ • クエリは、外の世界を変化させない • 何回呼んでも呼ばなくても構わない • ⇒ アローアンス Refs ॻ੶ʰ࣮ફςετۦಈ։ൃʱ / ୈ20ষ ςετͷΛௌ͘ ΫΤϦʹΞϩʔΞϯεɺίϚϯυʹΤΫεϖΫςʔγϣϯ
Slide 67
Slide 67 text
© - BASE, Inc. 改善する例
Slide 68
Slide 68 text
© - BASE, Inc. 改善する例 Anytimes()をcallすることで「呼んでも呼ばなくてもいい」 Stubになる
Slide 69
Slide 69 text
© - BASE, Inc. PRACTICE Real Practices in Go PRACTICE BASIC WHY カスタムアサーション テストダブルを使うテスト RDBMSを使う処理のテスト
Slide 70
Slide 70 text
© - BASE, Inc. RDBMSを使う処理の例
Slide 71
Slide 71 text
© - BASE, Inc. RDBMSを使う処理のテストの選択肢 • 期待したSQLを発⾏するかを検証する • 実際のDBは含めない、DBとの協調が期待通り か • ex: DATA-DOG/go-sqlmockの利⽤ • 実際のDBも検証範囲に含める • データベースを起動しテストに利⽤する • ex: lestrrat-go/test-mysqldの利⽤
Slide 72
Slide 72 text
© - BASE, Inc. 実感値: 期待したSQLを発⾏するかを検証する⽅法 • Props • テスト速度が速い • Cons • ⽋陥の発⽣可能性、期待値のSQLが間違ってい る • SQLの結果⾃体が責務の⼤部分を占める場合に 「SQLが正しい結果を得るものかどうか」を確 かめにくい
Slide 73
Slide 73 text
© - BASE, Inc. 実感値: 実際のDBも検証範囲に含める • Props • ⽋陥の発⽣可能性は下がる、 “安⼼感” も⾼い • 単体テストをデバッグに使うスタイルの場合、 DBを使えるのでやりやすい • Cons • テスト速度は遅い • 実際のDBを使えるようにするまでの準備はそ れなりに⼤変
Slide 74
Slide 74 text
© - BASE, Inc. The Test Automation Pyramid UI Service Unit Refs ॻ੶ʰSucceeding with Agileʱ / Chapter.16 Quality スコープの拡⼤ ⾃信の向上 ⾼速 分離性の向上
Slide 75
Slide 75 text
© - BASE, Inc. • ৽نςετ࡞ίετൃੜ • طଘςετҡ࣋ίετൃੜ •ςετ࣮ߦ࣌ؒͷ ͪίετൃੜ • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ ⽐較:RDBMSを使う処理のテストの選択肢 Unit Test ʹΑΔ ίετݮ Unit Test ͷ ࡞ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετݮ •ܽؕͷૣظൃݟʹΑΔ मਖ਼ίετݮ • υΩϡϝϯςʔγϣϯίετݮ •σόοάίετݮ • ઃܭվળʹΑΔϝϯςφϯείετݮ 実際のDBも検証範囲に 含める 期待したSQLを発⾏す るかを検証する⽅法
Slide 76
Slide 76 text
© - BASE, Inc. See also 期待したSQLを発⾏するかを検証する Refs https://devblog.thebase.in/entry/2018/12/04/110000
Slide 77
Slide 77 text
© - BASE, Inc. See also 実際のDBも検証範囲に含める Refs https://medium.com/@timakin/go-api-testing-173b97fb23ec • timakin さんのこちらの記事が参考になります。 • 「GoのAPIのテストにおける共通処理」 • https://medium.com/@timakin/go-api- testing- b fb ec
Slide 78
Slide 78 text
© - BASE, Inc. まとめ • 費⽤対効果の良いテストを⽬指す • 費⽤対効果のいいテストを⽬指す考えかたとし て、Goの考え⽅は有効 • 実際の現場での試⾏錯誤も、⽬的‧基礎を腹に 持っておくといい
Slide 79
Slide 79 text
© - BASE, Inc. fin.