https://gocon.jp/2022spring/sessions/a10-s/
Go Conference 2022 Springの登壇資料です。 資料の中のリンクはブログで紹介しています。
https://budougumi0617.github.io/2022/04/25/gocon2022spring/
© 2012-2022 BASE, Inc. 1#goconA@budougumi0617testingパッケージを使ったWebアプリケーションテスト単体テストからE2EテストまでGo Conference 2022 SpringBASE株式会社清水 陽一郎(@budougumi0617)
View Slide
© 2012-2022 BASE, Inc. 2#goconA@budougumi0617自己紹介所属BASE 株式会社 BASE BANKチーム Tech LeadGo、New Relicや自動化など。趣味ブログ: https://budougumi0617.github.io/SNSTwitter : @budougumi0617 Github : https://github.com/budougumi06172022/07/20 商業誌出版予定清水 陽一郎 (しみず よういちろう)
© 2012-2022 BASE, Inc. 3123#goconA@budougumi0617WHY - なぜテストするのか?WHAT - 何をテストするのか?HOW - どうテストするのか?今日の話
© 2012-2022 BASE, Inc. 4123#goconA@budougumi0617テストを書くことで自信をもって変化を積み上げるテストと設計は表裏一体なぜテストするのか意識する今日伝えたいこと
© 2012-2022 BASE, Inc. 5#goconA@budougumi0617● 「テストがないコードはレガシーコード」だから?● プロジェクトにカバレッジの基準があるから?Why Test
© 2012-2022 BASE, Inc. 6#goconA@budougumi0617● テストを書いたほうがいいことはみんな知っている● “golang やりたいテスト形式”でググればHOWはだいたい出てくる● 「なぜ」このやり方のテストを選択しているのか?が参考になれば🙏「なぜ」が一番大事
© 2012-2022 BASE, Inc. 7#goconA@budougumi0617● テストピラミッド● アジャイルテストの4象限● 様々なテストを黄金比通りに作ればいいのか?● 「あったほうがいい」からやるのか?● プロダクトの品質にどう寄与しているのかテストを書くhttps://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.htmlhttps://www.informit.com/articles/article.aspx?p=2253544&ranMID=24808
© 2012-2022 BASE, Inc. 8#goconA@budougumi0617● バグを補足できる● 変化を可能とする○ テストがあれば、検証しながらコードの動きを素早く変更できる○ テストがなければ、コードが良くなっているのか悪くなっているのかが本 当にはわからない。● レビューやデバッグの効率化● 結合度を低下させ、柔軟性をもたせる● 実装前にAPIを観察できるWhy Test
© 2012-2022 BASE, Inc. 9#goconA@budougumi0617● ネットショップ 作 成 サービスBASEオーナー様向けサービス● BASEの 売 上 をそのままVisa提携店で使える● GoはAPIサーバお題: BASEカード
© 2012-2022 BASE, Inc. 10#goconA@budougumi0617● リポジトリ○ 永続化処理、外部APIを実行するためレイヤー● ユースケース○ 永続化処理やドメインロジックを組み合わせてユースケースを表現するレイヤー● HTTPハンドラー○ JSONパース/レンダリングするレイヤーざっくり3層アーキテクチャ
© 2012-2022 BASE, Inc. 11© 2012-2022 BASE, Inc. 11リポジトリ関連のテスト
© 2012-2022 BASE, Inc. 12#goconA@budougumi0617● DB処理用のメソッド● UseCaseからコネクションをもらう実装○ トランザクション制御をユースケース上でするためリポジトリ関連のテスト
© 2012-2022 BASE, Inc. 13#goconA@budougumi0617● 実DBを使ってテストしている○ sqlxを使ってCASE式などを使ったSQLを書いているから○ gormやentなら不要かもしれない● sql-mock○ 期待通りのSQLが発行されただけ○ 実際にデータが取れるかはわからない● fixtureはシンプルなカタチで用意リポジトリ関連のテスト
© 2012-2022 BASE, Inc. 14#goconA@budougumi0617● CI上で実行する時などは環境変数をみる○ CircleCIだったらCIRCLECIが定義されている● Cleanupでレコードを消すリポジトリ関連のテスト
© 2012-2022 BASE, Inc. 15© 2012-2022 BASE, Inc. 15ユースケースのテスト
© 2012-2022 BASE, Inc. 16#goconA@budougumi0617● ユースケースレイヤーでトランザクション制御○ 抽象化による難読化を避けるため○ 参考: https://speakerdeck.com/sonatard/ideal-and-reality-of-abstraction-by-repositoryユースケース関連のテスト
© 2012-2022 BASE, Inc. 17#goconA@budougumi0617● 入力に対する期待シナリオでリポジトリ層やロジックが呼ばれているか○ 実DBは使っていない○ sql.DBオブジェクトやTxをモックしている■ Commit()が実行されたか○ 永続化データが正しくロールバックされるかはDBの実装による■ この層ではトランザクションをリポジトリに渡したか、Commit()を呼んだのかRollback()を呼んだのか分かればよいユースケース関連のテスト
© 2012-2022 BASE, Inc. 18© 2012-2022 BASE, Inc. 18HTTPハンドラーのテスト
© 2012-2022 BASE, Inc. 19#goconA@budougumi0617● JSONパースとJSONレンダリングくらいの実装HTTPハンドラーのテスト
© 2012-2022 BASE, Inc. 20#goconA@budougumi0617● httptest pkgを使った素朴なテスト● 入力/期待JSONをファイルにしている○ API仕様を理解しやすい■ testdata● card_setting○ request■ normal_case.json○ response■ ok.json■ invalid_setting.json● issue_card● …HTTPハンドラーのテスト
© 2012-2022 BASE, Inc. 21© 2012-2022 BASE, Inc. 21APIクライアントのテスト
© 2012-2022 BASE, Inc. 22#goconA@budougumi0617● モックサーバを自作してhttptest.Serverで起動している● http.ClientのTransport(RoundTripper)をすげ替える選択肢もある● 再利用性の観点で自作している○ 外部連携先に接続できない開発環境でダミーサーバとして利用するためAPIクライアントのテスト
© 2012-2022 BASE, Inc. 23© 2012-2022 BASE, Inc. 23E2Eテスト
© 2012-2022 BASE, Inc. 24#goconA@budougumi0617● カード決済の難しさ(ひとつの決済に複数の通信が行われる)○ オーソリ(残高確定)○ 確定○ 強制確定■ 為替変動による決済金額変動○ 強制確定(マイナス)■ 確定後にキャンセル■ 一部だけキャンセル(年間払いを途中でキャンセルetc)シミュレーションテスト(E2Eテスト)
© 2012-2022 BASE, Inc. 25#goconA@budougumi0617● デグレなしでエッジケース対応したい● 決済レコードに状態を持たせたテストがしたい○ オーソリ、確定、強制確定(マイナス)を処理した決済レコードのステータス● 複数回リクエストを処理した結果なのでユニットテストでは検証できない● 事前データを作る形式では”都合の良い”データになっている可能性シミュレーションテスト(E2Eテスト)
© 2012-2022 BASE, Inc. 26#goconA@budougumi0617● メルカリの柴田さんが書いていたE2Eフレームワークの簡易版○ https://engineering.mercari.com/blog/entry/gears-microservices/● 実DBに接続した本番同等のルーティングをhttptest.Serverで起動● 決済業者を模して残高確認webhookを複数回実行● DB上のレコードを検証シミュレーションテスト(E2Eテスト)
© 2012-2022 BASE, Inc. 27#goconA@budougumi0617● テストケースの実装など、メンテナビリティは低い● 問題ない○ ( 新 たなエッジケースの)テストケースが 追 加 されることはあっても、既存のテストケースが変わることはない○ 仕様が変わる時 == クレジットカードの決済の仕組みが変わるとき○ 完全なブラックボックステストなので脆くない● テストがあることで決済ロジックも安心して変更できるシミュレーションテスト(E2Eテスト)
© 2012-2022 BASE, Inc. 28123#goconA@budougumi0617テストを書くことで自信をもって変化を積み上げるテストと設計は表裏一体なぜテストするのか意識するまとめ
© 2012-2022 BASE, Inc. 29© 2012-2022 BASE, Inc. 29Tips
© 2012-2022 BASE, Inc. 30#goconA@budougumi0617● go test shuffle=on ./…○ 順不同に実行される○ non-parallelなテーブルサブテストは逐次のまま■ mapにするとよい● go test -count=1 ./…○ キャッシュを無視できる○ 競合状態の再現のときなど○ 明示的にキャシュを消すならば go clean -testcacheTips: よく使うgo testオプション
© 2012-2022 BASE, Inc. 31#goconA@budougumi0617● go test -race ./…○ 競合状態を発見してくれる○ テスト実行中に"実際に起きない"と検知できない○ CI上の実行ではひとまずつけておいてよいTips: よく使うgo testオプション
© 2012-2022 BASE, Inc. 32#goconA@budougumi0617● t.Helper()● t.Cleanupが増えてすっきり書ける● 共通化○ JSONファイル比較アサーション○ 事前データの流し込み関数Tips: ヘルパー
© 2012-2022 BASE, Inc. 33#goconA@budougumi0617● export_testを参照できるのはそのパッケージか..._testパッケージだけ○ Go Fridayこぼれ話:非公開(unexported)な機能を使ったテスト■ https://engineering.mercari.com/blog/entry/2018-08-08-080000/● go test ./…○ パッケージごとに別プロセスで動く(パッケージ間でメモリ空間が独立)○ 並行テストの動き方■ Go言語でのテストの並列化 〜t.Parallel()メソッドを理解する〜● https://engineering.mercari.com/blog/entry/how_to_use_t_parallel/● testdataディレクトリ○ The go tool will ignore a directory named "testdata", making it available to hold ancillary data needed by the tests.Tips:
© 2012-2022 BASE, Inc. 34#goconA@budougumi0617● github.com/golang/mock○ Goにおけるモックのデファクトツール○ モックメソッドの引数に指定した構造体の差分が見にくい時■ github.com/budougumi0617/cmpmock● github.com/DATA-DOG/go-sqlmock○ DBをモックしてくれる○ いい感じに初期化されたsql.Txがほしいときなどに使う。■ sql.Txは適切に初期化されていないとCommitなど実行したときに失敗するTips: OSS
© 2012-2022 BASE, Inc. 35#goconA@budougumi0617● github.com/google/go-cmp○ 構造体の比較をよしなにしたいとき○ cmpoptsサブパッケージはちゃんと見ておいたほうがよい○ SortMaps, IgnoreFields, IgnoreTypes etc…● github.com/k1LoW/octocov○ しゅっとカバレッジ可視化できるTips: OSS
© 2012-2022 BASE, Inc. 36#goconA@budougumi0617● github.com/stretchr/testify○ https://pkg.go.dev/github.com/stretchr/testify/assert#Eventually● github.com/onsi/gomega○ https://pkg.go.dev/github.com/onsi/gomega#Eventually○ イベント駆 動な結 果 整 合 性までテストしたい場 合は素 直にtestifyやgomegaをつかったほうがよさそうTips: OSS