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

Goプロダクトにおけるテスト改善の軌跡

kiyoshiro
December 12, 2024
150

 Goプロダクトにおけるテスト改善の軌跡

https://dmm.connpass.com/event/335033/ のDMM.goのトラックで発表した内容

kiyoshiro

December 12, 2024
Tweet

Transcript

  1. Goプロダクトにおけるテスト改善の軌跡 自己紹介
 名前
 清川航一 (𝕏 @kiyoshiro944)
 おしごと
 ・おもにフロントエンド(React/TypeScript 6年ほど) 👈のちの伏線


    ・バックエンドもぼちぼち書く(Go歴 3年ほど) 
 ・分析クエリ書いたりも 
 所属
 
                       ポイントクラブチーム 
                       新卒3年目 
 
 
 
 2
  2. Goプロダクトにおけるテスト改善の軌跡 課題:出力値(actual)のコピペがつらい
 
 • 
 改善1. ゴールデンテスト導入 - 課題
 🤔「どんなエラー文が返ってくるかわからない」

    💪「一旦落ちる前提でテスト実行してみて、 actualの値 をコピってくるか」 ※今回の例ではそもそも完全一致でアサーションしているのが問題では あります。実際はもっと事情が複雑でしたが割愛。 そしてある日、仕様変更によりテストが 20〜30箇所落ちた
 😇「これ全部出力値のコピペで修正するのか…」 
 👉 6
  3. Goプロダクトにおけるテスト改善の軌跡 自分🤔「フロントエンドだったらスナップショットテスト使うかもな」 
 スナップショットテスト(Go界隈ではゴールデンテスト )
 改善1. ゴールデンテスト導入 - 改善
 7

    テスト対象
 入力
 出力
 テスト1回目
 
 
 { “a”: 1, “b”: 2 } ファイルに保存
 テスト対象
 
 
 入力
 出力
 テスト2回目以降
 
 
 ゴールデンファイル
 一致するか比較

  4. Goプロダクトにおけるテスト改善の軌跡 改善1. ゴールデンテスト導入 - ライブラリの選定
 8 cupaloy
 goldie
 自前でヘルパー関数つくる 


    ⭐300ちょっと
 ⭐200ちょっと
 ー
 最終コミット3ヶ月前
 その前のコミットは1年前 
 最終コミット4ヶ月前
 その前のコミットは2年前 
 ー
 ・どちらも開発が活発とは言えない状態だった 😢
 ・機能・APIに大差はなかった 
 現実的な工数で作れそうでは あるが大変
 ・ゴールデンファイルがなければ作成。あれば 比較する処理を実装
 ・ゴールデンファイルを書き出すディレクトリ名、 ファイル名を決定
 ※選定を行った2022/12当時のデータ。他にもライブラリはたくさんあったが当時は3つのみ比較した 
 ※納期が迫っており、選定にそこまで工数をかけられなかった 😢

  5. Goプロダクトにおけるテスト改善の軌跡 • 良かった点 
 👍ゴールデンテストの導入により、出力値のコピペをしなくてよくなった
 👍ライブラリ更新などで出力値が変わったときもゴールデンファイルを
 コマンド一発で更新 できる
 • 注意点


    • 更新が簡単な分、ゴールデンファイルは思考停止で更新されがち。
 コードレビューで、差分が妥当かも確認する 必要あり
 • ゴールデンファイルが大きくなりすぎると、この差分の確認が大変なので、入力 値を調整する必要あり
  󰢃出力が1000件のスライス
  󰢏出力が5件のスライス
 改善1. ゴールデンテスト導入 - 導入してみて
 9
  6. Goプロダクトにおけるテスト改善の軌跡 13 • 外部の人にも伝わるように仕様をシンプル にしたもの(※実サービスの仕様とは異なります) 
 • ゲームクリア時に新規ユーザーには100pt, それ以外には1pt付与する実装 


    • 正常系テストでエラーを返さないことだけ 検 証
 
 課題:検証がゆるい 
 何ポイント付与したか検証できておらず、バグ を検知できない
 ※Repositoryなら集約の単位で保存しろなどツッコミもあると思いますが、本筋と関係ないので一旦スルーを 🙏

  7. Goプロダクトにおけるテスト改善の軌跡 生成されたモックの使い方 - 2⃣gomock or mockery
 21 ※使い方がほぼ一緒のためgomockのみ記載 
 👍引数の検証、呼び出し回数の検証がシンプルに書ける

    
 (Issueメソッドの呼び出し回数がきっかり1回であることも検証できている) 
 gomockとmockeryの細かい違いは“Comparison of golang mocking libraries”のGistを見ると良い 
 https://gist.github.com/maratori/8772fe158ff705ca543a0620863977c2 
 

  8. Goプロダクトにおけるテスト改善の軌跡 • 課題
 返り値がerrorのみのメソッドでモックの引数が検証できておらず検証がゆるかった。 またモックの手書きは変更が大変だった。
 • 解決法
 モック自動生成ライブラリmoqを導入
 • 学び


    カバレッジは「高いから安心」ではない
 取得以外の処理(Saveとか)のモックで以下の2つを検証するのが大事
 • 想定通りの引数で呼ばれたか
 • 想定通りの回数呼ばれたか
 改善2. モックの引数検証 - まとめ
 23
  9. Goプロダクトにおけるテスト改善の軌跡 改善3. 過剰なテーブル駆動をやめる - 注意
 • この章は今までよりも長いです
 • とくに「課題→改善方法→まとめ」のうち「課題」のパートが長い です


    • Go Conference 2024でも似た発表があったので、気になるかた はそちらもチェックを!
 • 『Table-driven testing に縛られないGoのテストパターン』 (https://gocon.jp/2024/sessions/19/)
 25
  10. Goプロダクトにおけるテスト改善の軌跡 読みづらいポイント2: しれっと書いてある共通処理
 41 👈❗❗ ❗ (※ログインユーザーのIDに1をセットしている) 
 
 全テストケースに共通して発動する処

    理がfor文の中 に書かれていることが あるので、読み飛ばせない 
 ※たまにこの共通処理がテストのwant値に影響す ることがある。油断ならない。

  11. Goプロダクトにおけるテスト改善の軌跡 • 困ったときは原点に立ち返ってGo Wikiのテーブル駆動の説明を読もう
 改善3. 過剰なテーブル駆動をやめる - 改善方法の模索
 良いテストを書くことは簡単ではありませんが、多くの状況ではテーブ ル駆動テストでカバーできます。(...中略...)


    テストを書くときにコピペしてる場合は、テーブル駆動テストにリファクタ リングするか、コピーしたコードをヘルパー関数に切り出すかを検討し てください
 (https://go.dev/wiki/TableDrivenTests より)
 49
  12. Goプロダクトにおけるテスト改善の軌跡 気づき
 • 実は「すべてのテストをテーブル駆動で書け」とまでは言われていない
 • 「1メソッドにつき1テーブルにまとめろ」とも言われていない
 改善3. 過剰なテーブル駆動をやめる
 良いテストを書くことは簡単ではありませんが、多くの状況では テーブ

    ル駆動テストでカバーできます。(...中略...)
 テストを書くときにコピペしてる場合は、テーブル駆動テストにリファクタ リングするか、コピーしたコードをヘルパー関数に切り出すかを検討し てください
 (https://go.dev/wiki/TableDrivenTests より)
 50
  13. Goプロダクトにおけるテスト改善の軌跡 • フロントエンドでもテーブル駆動のような書き方はあるが、同じような検証 を複数の入力値 で行うときにしか使わない。
 
 
 
 
 


    
 
 
 
 • 『単体テストの考え方/使い方』ではテーブル駆動に近いものとして「パラメータ化テスト」が 紹介されている(p. 83)。ここでも「同じようなテストケースを 1つのグループにまとめる」と書 かれている
 他の界隈も見てみると…
 51 (Jest公式ドキュメント より引用)

  14. Goプロダクトにおけるテスト改善の軌跡 • 重複をどうするか…
 • もう一回Go Wikiを読むと…
 改善3. 過剰なテーブル駆動をやめる
 良いテストを書くことは簡単ではありませんが、多くの状況ではテーブル駆動テストでカ バーできます。(...中略...)

    
 テストを書くときにコピペしてる場合は、テーブル駆動テストに リファクタリングするか、コピーしたコードをヘルパー関数に 切り出すか を検討してください
 (https://go.dev/wiki/TableDrivenTests より)
 58
  15. Goプロダクトにおけるテスト改善の軌跡 • テストケースを追加するときのルール
 1.まずはテーブル駆動なし で検証したい項目ごとにt.Runを書く
 2.同じパターンのみ テーブル駆動でまとめる
 3.テストコードの重複を抑えたい場合はヘルパー関数 に切り出す
 


    👉今までは、1テストメソッドにつきテーブルは1個だったのが、基本的には0個に なった(テストによっては2個以上になることもある)
 改善3. 過剰なテーブル駆動をやめる - 現状の方針
 61
  16. Goプロダクトにおけるテスト改善の軌跡 1メソッドのテストにつき1テーブルにしているせいで 
 読みづらいポイント 
  1.上から下の順に読めない 
  2.しれっとfor文の中に書いてある共通処理 
  3.同じようなテストコードが連続して、もはや間違い探し 

    👈※ルール1だけだとより悪化する場合がある 
 書きづらいポイント 
  1.まずはテーブルを考える必要あり 
  2.毛色の違うアサーションを足しづらい 
 改善3. 過剰なテーブル駆動をやめる - 課題感(再掲)
 63 ルール1.まずはテーブル駆動なし で検証したい項目ごとにt.Runを書く
  17. Goプロダクトにおけるテスト改善の軌跡 1メソッドのテストにつき1テーブルにしているせいで 
 読みづらいポイント 
  1.上から下の順に読めない 
  2.しれっとfor文の中に書いてある共通処理 👈※want値に影響を与える共通処理はfor文の中に書かないように
  3.同じようなテストコードが連続して、もはや間違い探し

    
 書きづらいポイント 
  1.まずはテーブルを考える必要あり 
  2.毛色の違うアサーションを足しづらい 
 改善3. 過剰なテーブル駆動をやめる - 課題感(再掲)
 64 ルール2.同じパターンのみ テーブル駆動でまとめる
 ルール3.テストコードの重複を抑えたい場合はヘルパー関数 に切り出す
  18. Goプロダクトにおけるテスト改善の軌跡 • 課題
 同一ではないパターンを1テーブルに無理やり押し込んでおり、読みづらい・書きづら い問題が起きていた。
 • 改善方法
 基本的にはテーブル駆動を使わずにテストを書き、同一パターンだけ小さなテーブル 駆動に切り出す。
 •

    学び
 まずは有名な手法に従うのが大切。ただし、盲目的に受け入れるのではなく、一度先 入観をなくしてより良い方法を模索するのも大切。
 改善3. 過剰なテーブル駆動をやめる - まとめ
 65
  19. Goプロダクトにおけるテスト改善の軌跡 全体のまとめ
 66 
 課題
 改善方法 
 1
 テスト出力値のコピペが辛かった。 


    cupaloyというライブラリを採用しゴールデンテストを 導入した。
 2
 モックの引数が検証できておらずバグを見逃す&手書き したモックの変更が大変だった。 
 モック自動生成ライブラリmoqを導入した。 
 
 3
 同一ではないパターンを1テーブルに無理やり押し込ん でおり、読みづらい・書きづらい問題が起きていた。 
 基本的にはテーブル駆動を使わずにテストを書き、 同一パターンだけ小さなテーブル駆動に切り出す。 

  20. Goプロダクトにおけるテスト改善の軌跡 • 自分にとって人生はじめてのGoプロダクトだったので、最初は目にするコードすべて が正解に見えた。それでも課題を見つけられるようになったのは、
 • フロントエンドなど他の界隈の知識と比較 したから
 • 言語に関係ない汎用的な知識 をつけたから

    
 (『単体テストの考え方/使い方』めっちゃおすすめです!!!) 
 • 今回のようになにか改善を加えるときは、メンバーに少しずつ試してもらってフィード バックをもらう のが大事
 • 複数人が良さを実感したものをチームに広げていく
 • 評判が良くなかったらもとのやり方に切り戻す
 あとがき
 68
  21. Goプロダクトにおけるテスト改善の軌跡 改善3. 過剰なテーブル駆動をやめる
 • 自分たちも含め1メソッドにつき1つのテーブル駆動で書いているプロダクトは DMMの中でも結構見かける。なぜ🤔
 • Go Wikiではヘルパー関数は解説がほぼない一方でテーブル駆動は1ページまる まる使って丁寧に解説されている


    • 「テーブル駆動」という覚えやすい名前もあいまって認知しやすく使われやす くなった
 『ハンマーを持つ人にはすべてが釘に見える』
 その他、認知科学分野で、名前がついていると認知しやすくなる現象などを 引用
 71