Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cost-effective Go Unit Test thinking and practices

Cost-effective Go Unit Test thinking and practices

Go Conference’19 Summer in Fukuoka で費用対効果の良いテストを目指すための目的・基礎・実践について話したものです

Avatar for Kazuki Higashiguchi

Kazuki Higashiguchi

July 13, 2019
Tweet

More Decks by Kazuki Higashiguchi

Other Decks in Programming

Transcript

  1. © - BASE, Inc. X Cost-effective Go Unit Test thinking

    and practices Go Conference’ Summer in Fukuoka . . - @hgsgtk
  2. © - BASE, Inc. Talk Structure Cost-effective Unit Test Go

    Unit Test Basic Real Practices in Go WHY BASIC PRACTICE
  3. © - BASE, Inc. : @hgsgtk ⾃⼰紹介 東⼝ 和暉 (

    Higashiguchi Kazuki ) Go, PHP, Python BASE BANK, Inc. (BASE, Inc.) / Dev Division / Tech Lead
  4. © - BASE, Inc. Talk Structure Cost-effective Unit Test Go

    Unit Test Basic Real Practices in Go WHY BASIC PRACTICE
  5. © - BASE, Inc. Unit Testとは • プログラムを構成する⽐較的⼩さな単位(ユニッ ト)が個々の機能を正しく果たしているかどうか を検証する

    • 実⾏⼿段として、⼿動によるテスト‧⾃動化テス トが存在する • このトークでは、⾃動化テストを取り上げる
  6. © - BASE, Inc. WHY? なぜUnit Testを書くのか • ⽋陥混⼊の阻⽌、バグを防ぎたい? •

    テストによるドキュメンテーション? • 設計改善の指標?
  7. © - BASE, Inc. • ৽نςετ࡞੒ίετൃੜ • طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷ଴ͪίετൃੜ

    • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ ユニットテストの両⾯のコスト Unit Test ʹΑΔ ίετ࡟ݮ Unit Test ͷ ࡞੒ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετ࡟ݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετ࡟ݮ • υΩϡϝϯςʔγϣϯίετ࡟ݮ • σόοάίετ࡟ݮ • ઃܭվળʹΑΔϝϯςφϯείετ࡟ݮ
  8. © - BASE, Inc. • ৽نςετ࡞੒ίετൃੜ • طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷ଴ͪίετൃੜ

    • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ コストへの集約 Unit Test ʹΑΔ ίετ࡟ݮ Unit Test ͷ ࡞੒ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετ࡟ݮ •ܽؕͷૣظൃݟʹΑΔ मਖ਼ίετ࡟ݮ • υΩϡϝϯςʔγϣϯίετ࡟ݮ • σόοάίετ࡟ݮ • ઃܭվળʹΑΔϝϯςφϯείετ࡟ݮ ⽋陥混⼊の阻⽌、バグを防ぎたい?
  9. © - BASE, Inc. • ৽نςετ࡞੒ίετൃੜ • طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷ଴ͪίετൃੜ

    • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ Unit Test ʹΑΔ ίετ࡟ݮ Unit Test ͷ ࡞੒ɾҡ࣋ίετ VS テストによるドキュメンテーション? • खಈϢχοτςετͷίετ࡟ݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετ࡟ݮ •υΩϡϝϯςʔγϣϯ ίετ࡟ݮ • σόοάίετ࡟ݮ • ઃܭվળʹΑΔϝϯςφϯείετ࡟ݮ コストへの集約
  10. © - BASE, Inc. • ৽نςετ࡞੒ίετൃੜ • طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷ଴ͪίετൃੜ

    • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ Unit Test ʹΑΔ ίετ࡟ݮ Unit Test ͷ ࡞੒ɾҡ࣋ίετ VS 設計改善の指標? • खಈϢχοτςετͷίετ࡟ݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετ࡟ݮ • υΩϡϝϯςʔγϣϯίετ࡟ݮ • σόοάίετ࡟ݮ •ઃܭվળʹΑΔ ϝϯςφϯείετ࡟ݮ コストへの集約
  11. © - BASE, Inc. Cost effective Unit Test Unit Test

    ʹΑΔ ίετ࡟ݮ Unit Test ͷ ࡞੒ɾҡ࣋ίετ >
  12. © - BASE, Inc. Talk Structure Cost-effective Unit Test Go

    Unit Test Basic Real Practices in Go WHY BASIC PRACTICE
  13. © - BASE, Inc. BASIC Go Unit Test Basic •

    適切なエラーレポート • 適切なエラーハンドリング • テーブル駆動テスト‧サブテスト BASIC
  14. © - BASE, Inc. BASIC Go Unit Test Basic •

    適切なエラーレポート • 適切なエラーハンドリング • テーブル駆動テスト‧サブテスト BASIC
  15. © - 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
  16. © - BASE, Inc. 不適切なエラーレポートの弊害 . 「どこで何を検証して失敗したのか」が読みとり にくい . ⼊⼒‧期待値が結果から読み取れない

    . 問題が起きたか知るためにテストコード‧動作 コードを読む必要がある --- FAIL: TestInStatusListInAppropriateErrorReport ( . s) simple_test.go: : unexpected value true
  17. © - 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.” 直接的 かつ 適切
  18. © - BASE, Inc. 例題を改善する 直接的かつ適切なエラーレポートを⾏う ex. “f(x) = y,

    want z”という⼀つの形式 f(x): 試みた結果と⼊⼒ y: 得られた結果 z: 期待値
  19. © - 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
  20. © - BASE, Inc. • ৽نςετ࡞੒ίετൃੜ •طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷ଴ͪίετൃੜ •

    ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ 適切なエラーレポートの効果 Unit Test ʹΑΔ ίετ࡟ݮ Unit Test ͷ ࡞੒ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετ࡟ݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετ࡟ݮ • υΩϡϝϯςʔγϣϯίετ࡟ݮ • σόοάίετ࡟ݮ • ઃܭվળʹΑΔϝϯςφϯείετ࡟ݮ テスト失敗時の 原因調査コストの削減
  21. © - BASE, Inc. BASIC Go Unit Test Basic •

    適切なエラーレポート • 適切なエラーハンドリング • テーブル駆動テスト‧サブテスト BASIC
  22. © - 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
  23. © - BASE, Inc. • ৽نςετ࡞੒ίετൃੜ •طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷ଴ͪίετൃੜ •

    ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ 適切なエラーハンドリングの効果 Unit Test ʹΑΔ ίετ࡟ݮ Unit Test ͷ ࡞੒ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετ࡟ݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετ࡟ݮ • υΩϡϝϯςʔγϣϯίετ࡟ݮ • σόοάίετ࡟ݮ • ઃܭվળʹΑΔϝϯςφϯείετ࡟ݮ 致命的なもののみをcrashさ せるため、継続可能な限り、 ⼀回の実⾏で多く検証結果を 得られる
  24. © - BASE, Inc. BASIC Go Unit Test Basic •

    適切なエラーレポート • 適切なエラーハンドリング • テーブル駆動テスト‧サブテスト BASIC
  25. © - 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
  26. © - BASE, Inc. •৽نςετ࡞੒ίετൃੜ •طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷ଴ͪίετൃੜ • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ

    • Ϣχοτςετͷֶशίετൃੜ テーブル駆動テスト‧サブテストの効果 Unit Test ʹΑΔ ίετ࡟ݮ Unit Test ͷ ࡞੒ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετ࡟ݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετ࡟ݮ • υΩϡϝϯςʔγϣϯίετ࡟ݮ •σόοάίετ࡟ݮ • ઃܭվળʹΑΔϝϯςφϯείετ࡟ݮ サブテストによる特定テストケースの実⾏
  27. © - BASE, Inc. •৽نςετ࡞੒ίετൃੜ •طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷ଴ͪίετൃੜ • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ

    • Ϣχοτςετͷֶशίετൃੜ テーブル駆動テスト‧サブテストの効果 Unit Test ʹΑΔ ίετ࡟ݮ Unit Test ͷ ࡞੒ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετ࡟ݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετ࡟ݮ • υΩϡϝϯςʔγϣϯίετ࡟ݮ •σόοάίετ࡟ݮ • ઃܭվળʹΑΔϝϯςφϯείετ࡟ݮ テストパターンと検証ロジッ クの分離の効果 同じ検証ロジックを何度も書 かずに済む
  28. © - BASE, Inc. •৽نςετ࡞੒ίετൃੜ •طଘςετҡ࣋ίετൃੜ • ςετ࣮ߦ࣌ؒͷ଴ͪίετൃੜ • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ

    • Ϣχοτςετͷֶशίετൃੜ テーブル駆動テスト‧サブテストの効果 Unit Test ʹΑΔ ίετ࡟ݮ Unit Test ͷ ࡞੒ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετ࡟ݮ • ܽؕͷૣظൃݟʹΑΔमਖ਼ίετ࡟ݮ • υΩϡϝϯςʔγϣϯίετ࡟ݮ •σόοάίετ࡟ݮ • ઃܭվળʹΑΔϝϯςφϯείετ࡟ݮ エントリ追加で テストのバリエーションを修 正できる
  29. © - BASE, Inc. Talk Structure Cost-effective Unit Test Go

    Unit Test Basic Real Practices in Go WHY BASIC PRACTICE
  30. © - BASE, Inc. PRACTICE Real Practices in Go PRACTICE

    BASIC WHY カスタムアサーション テストダブルを使うテスト RDBMSを使う処理のテスト
  31. © - BASE, Inc. PRACTICE Real Practices in Go PRACTICE

    BASIC WHY カスタムアサーション テストダブルを使うテスト RDBMSを使う処理のテスト
  32. © - BASE, Inc. テストコードが増えてくると • HTTP Handlerなど関⼼事が領域が近い 処理のテストコードは、同じ関⼼領域を 類似した検証ロジックでテストする

    • コピペが⽣まれると往々にして「適切な エラーレポート」が忘れられる • ⇒ テストの維持コストの増加
  33. © - BASE, Inc. 実例:HTTP Handlerのアサーションを共通処理にする • t: *testing.T を求め、t.Helper()をcall

    • testTarget: 「どのHandlerを施⾏するか」を設定 • gotRes: 試⾏結果の *http.Response • wantCode, bodyFile: 期待値
  34. © - BASE, Inc. 実例:HTTP Handlerのアサーションを共通処理にする • testdata ディレクトリを作成 •

    ⇒パッケージとしてみなされない、テスト時のみ必要なファ イルであることが明⽰的になる • *.json.golden 拡張⼦として配置 • ⇒ 標準パッケージでも使われている⼿法 • ⇒テストのドキュメントとしての価値の向上
  35. © - BASE, Inc. PRACTICE Real Practices in Go PRACTICE

    BASIC WHY カスタムアサーション テストダブルを使うテスト RDBMSを使う処理のテスト
  36. © - BASE, Inc. テストダブル Test Double: テスト固有の同等物 Mock: テスト対象に適切に使⽤されているか検証

    する Refs ॻ੶ʰxUnit Test Patterns: Refactoring Test Codeʱ / Chapter 23. Test Double Patterns
  37. © - BASE, Inc. ※補⾜:このトーク内での “Mock” Refs ॻ੶ʰςετۦಈ։ൃʱ / ෇࿥C

    Ϣχοτςετपลͷ஌ࣝͷ੔ཧͱTDD֦ுͷࢼΈ • xUTPの語彙整理により、「広義のMock Object」‧「狭義のMock Object」を分けて議 論できるようになった • このトークでの “Mock” もTest Doubleの範囲を ざっくりと含める「広義のMock Object」とする
  38. © - BASE, Inc. テストダブルを使うテスト 次のような属性を持つものに依存する場合に、テス ト時にテストダブルに置き換える事がある • 結果が不定 •

    ex: UUID • システム外コンポーネントを利⽤する処理 • ex: API, RDBMS, AWS etc • レイヤ構造をまたぐ依存 • ex: Service->Repository
  39. © - BASE, Inc. テストダブル作成の選択肢としてのgomock • “GoMock is a mocking

    framework for the Go programming language.” • https://github.com/golang/mock
  40. © - BASE, Inc. 的確なエクスペクテーション Refs ॻ੶ʰ࣮ફςετۦಈ։ൃʱ / ୈ20ষ ςετͷ੠Λௌ͘

    “意図を明確にするためには、スタブとエクスペク テーション‧アサーションをきっちり区別するとよ い。”
  41. © - BASE, Inc. 区別するための指標:コマンド/クエリ Refs ॻ੶ʰ࣮ફςετۦಈ։ൃʱ / ୈ20ষ ςετͷ੠Λௌ͘

    ΫΤϦʹ͸ΞϩʔΞϯεɺίϚϯυʹ͸ΤΫεϖΫςʔγϣϯ • コマンドとは、 副作⽤を伴うことのある呼び出し • オブジェクトの外の世界を変化させる • ex. DBのレコード保存‧状態変化 • 複数回実⾏した場合、システム状態は違ったもの になる => エクスペクテーション
  42. © - BASE, Inc. 区別するための指標:コマンド/クエリ • クエリは、外の世界を変化させない • 何回呼んでも呼ばなくても構わない •

    ⇒ アローアンス Refs ॻ੶ʰ࣮ફςετۦಈ։ൃʱ / ୈ20ষ ςετͷ੠Λௌ͘ ΫΤϦʹ͸ΞϩʔΞϯεɺίϚϯυʹ͸ΤΫεϖΫςʔγϣϯ
  43. © - BASE, Inc. PRACTICE Real Practices in Go PRACTICE

    BASIC WHY カスタムアサーション テストダブルを使うテスト RDBMSを使う処理のテスト
  44. © - BASE, Inc. RDBMSを使う処理のテストの選択肢 • 期待したSQLを発⾏するかを検証する • 実際のDBは含めない、DBとの協調が期待通り か

    • ex: DATA-DOG/go-sqlmockの利⽤ • 実際のDBも検証範囲に含める • データベースを起動しテストに利⽤する • ex: lestrrat-go/test-mysqldの利⽤
  45. © - BASE, Inc. 実感値: 期待したSQLを発⾏するかを検証する⽅法 • Props • テスト速度が速い

    • Cons • ⽋陥の発⽣可能性、期待値のSQLが間違ってい る • SQLの結果⾃体が責務の⼤部分を占める場合に 「SQLが正しい結果を得るものかどうか」を確 かめにくい
  46. © - BASE, Inc. 実感値: 実際のDBも検証範囲に含める • Props • ⽋陥の発⽣可能性は下がる、

    “安⼼感” も⾼い • 単体テストをデバッグに使うスタイルの場合、 DBを使えるのでやりやすい • Cons • テスト速度は遅い • 実際のDBを使えるようにするまでの準備はそ れなりに⼤変
  47. © - BASE, Inc. The Test Automation Pyramid UI Service

    Unit Refs ॻ੶ʰSucceeding with Agileʱ / Chapter.16 Quality スコープの拡⼤ ⾃信の向上 ⾼速 分離性の向上
  48. © - BASE, Inc. • ৽نςετ࡞੒ίετൃੜ • طଘςετҡ࣋ίετൃੜ •ςετ࣮ߦ࣌ؒͷ ଴ͪίετൃੜ

    • ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ • Ϣχοτςετͷֶशίετൃੜ ⽐較:RDBMSを使う処理のテストの選択肢 Unit Test ʹΑΔ ίετ࡟ݮ Unit Test ͷ ࡞੒ɾҡ࣋ίετ VS • खಈϢχοτςετͷίετ࡟ݮ •ܽؕͷૣظൃݟʹΑΔ मਖ਼ίετ࡟ݮ • υΩϡϝϯςʔγϣϯίετ࡟ݮ •σόοάίετ࡟ݮ • ઃܭվળʹΑΔϝϯςφϯείετ࡟ݮ 実際のDBも検証範囲に 含める 期待したSQLを発⾏す るかを検証する⽅法
  49. © - 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