testingパッケージを使った Webアプリケーションテスト 単体テストからE2Eテストまで / gocon2022spring
by
Yoichiro Shimizu
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Slide 1
Slide 1 text
© 2012-2022 BASE, Inc. 1 #goconA @budougumi0617 testingパッケージを使った Webアプリケーションテスト 単体テストからE2Eテストまで Go Conference 2022 Spring BASE株式会社 清水 陽一郎(@budougumi0617)
Slide 2
Slide 2 text
© 2012-2022 BASE, Inc. 2 #goconA @budougumi0617 自己紹介 所属 BASE 株式会社 BASE BANKチーム Tech Lead Go、New Relicや自動化など。 趣味 ブログ: https://budougumi0617.github.io/ SNS Twitter : @budougumi0617 Github : https://github.com/budougumi0617 2022/07/20 商業誌出版予定 清水 陽一郎 (しみず よういちろう)
Slide 3
Slide 3 text
© 2012-2022 BASE, Inc. 3 1 2 3 #goconA @budougumi0617 WHY - なぜテストするのか? WHAT - 何をテストするのか? HOW - どうテストするのか? 今日の話
Slide 4
Slide 4 text
© 2012-2022 BASE, Inc. 4 1 2 3 #goconA @budougumi0617 テストを書くことで自信をもって変化を積み上げる テストと設計は表裏一体 なぜテストするのか意識する 今日伝えたいこと
Slide 5
Slide 5 text
© 2012-2022 BASE, Inc. 5 #goconA @budougumi0617 ● 「テストがないコードはレガシーコード」だから? ● プロジェクトにカバレッジの基準があるから? Why Test
Slide 6
Slide 6 text
© 2012-2022 BASE, Inc. 6 #goconA @budougumi0617 ● テストを書いたほうがいいことはみんな知っている ● “golang やりたいテスト形式”でググればHOWはだいたい出てくる ● 「なぜ」このやり方のテストを選択しているのか?が参考になれば🙏 「なぜ」が一番大事
Slide 7
Slide 7 text
© 2012-2022 BASE, Inc. 7 #goconA @budougumi0617 ● テストピラミッド ● アジャイルテストの4象限 ● 様々なテストを黄金比通りに作ればいいのか? ● 「あったほうがいい」からやるのか? ● プロダクトの品質にどう寄与しているのか テストを書く https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html https://www.informit.com/articles/article.aspx?p=2253544&ranMID=24808
Slide 8
Slide 8 text
© 2012-2022 BASE, Inc. 8 #goconA @budougumi0617 ● バグを補足できる ● 変化を可能とする ○ テストがあれば、検証しながらコードの動きを素早く変更できる ○ テストがなければ、コードが良くなっているのか悪くなっているのかが本 当にはわ からない。 ● レビューやデバッグの効率化 ● 結合度を低下させ、柔軟性をもたせる ● 実装前にAPIを観察できる Why Test
Slide 9
Slide 9 text
© 2012-2022 BASE, Inc. 9 #goconA @budougumi0617 ● ネットショップ 作 成 サービスBASE オーナー様向けサービス ● BASEの 売 上 をそのまま Visa提携店で使える ● GoはAPIサーバ お題: BASEカード
Slide 10
Slide 10 text
© 2012-2022 BASE, Inc. 10 #goconA @budougumi0617 ● リポジトリ ○ 永続化処理、外部APIを実行するためレイヤー ● ユースケース ○ 永続化処理やドメインロジックを組み合わせてユースケースを表現するレイヤー ● HTTPハンドラー ○ JSONパース/レンダリングするレイヤー ざっくり3層アーキテクチャ
Slide 11
Slide 11 text
© 2012-2022 BASE, Inc. 11 © 2012-2022 BASE, Inc. 11 リポジトリ関連のテスト
Slide 12
Slide 12 text
© 2012-2022 BASE, Inc. 12 #goconA @budougumi0617 ● DB処理用のメソッド ● UseCaseからコネクションをもらう実装 ○ トランザクション制御をユースケース上でするため リポジトリ関連のテスト
Slide 13
Slide 13 text
© 2012-2022 BASE, Inc. 13 #goconA @budougumi0617 ● 実DBを使ってテストしている ○ sqlxを使ってCASE式などを使ったSQLを書いているから ○ gormやentなら不要かもしれない ● sql-mock ○ 期待通りのSQLが発行されただけ ○ 実際にデータが取れるかはわからない ● fixtureはシンプルなカタチで用意 リポジトリ関連のテスト
Slide 14
Slide 14 text
© 2012-2022 BASE, Inc. 14 #goconA @budougumi0617 ● CI上で実行する時などは環境変数をみる ○ CircleCIだったらCIRCLECIが定義されている ● Cleanupでレコードを消す リポジトリ関連のテスト
Slide 15
Slide 15 text
© 2012-2022 BASE, Inc. 15 © 2012-2022 BASE, Inc. 15 ユースケースのテスト
Slide 16
Slide 16 text
© 2012-2022 BASE, Inc. 16 #goconA @budougumi0617 ● ユースケースレイヤーでトランザクション制御 ○ 抽象化による難読化を避けるため ○ 参考: https://speakerdeck.com/sonatard/ideal-and-reality-of-abstraction-by-repository ユースケース関連のテスト
Slide 17
Slide 17 text
© 2012-2022 BASE, Inc. 17 #goconA @budougumi0617 ● 入力に対する期待シナリオでリポジトリ層やロジックが呼ばれているか ○ 実DBは使っていない ○ sql.DBオブジェクトやTxをモックしている ■ Commit()が実行されたか ○ 永続化データが正しくロールバックされるかはDBの実装による ■ この層ではトランザクションをリポジトリに渡したか、Commit()を呼んだのかRollback()を呼 んだのか分かればよい ユースケース関連のテスト
Slide 18
Slide 18 text
© 2012-2022 BASE, Inc. 18 © 2012-2022 BASE, Inc. 18 HTTPハンドラーのテスト
Slide 19
Slide 19 text
© 2012-2022 BASE, Inc. 19 #goconA @budougumi0617 ● JSONパースとJSONレンダリングくらいの実装 HTTPハンドラーのテスト
Slide 20
Slide 20 text
© 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ハンドラーのテスト
Slide 21
Slide 21 text
© 2012-2022 BASE, Inc. 21 © 2012-2022 BASE, Inc. 21 APIクライアントのテスト
Slide 22
Slide 22 text
© 2012-2022 BASE, Inc. 22 #goconA @budougumi0617 ● モックサーバを自作してhttptest.Serverで起動している ● http.ClientのTransport(RoundTripper)をすげ替える選択肢もある ● 再利用性の観点で自作している ○ 外部連携先に接続できない開発環境でダミーサーバとして利用するため APIクライアントのテスト
Slide 23
Slide 23 text
© 2012-2022 BASE, Inc. 23 © 2012-2022 BASE, Inc. 23 E2Eテスト
Slide 24
Slide 24 text
© 2012-2022 BASE, Inc. 24 #goconA @budougumi0617 ● カード決済の難しさ(ひとつの決済に複数の通信が行われる) ○ オーソリ(残高確定) ○ 確定 ○ 強制確定 ■ 為替変動による決済金額変動 ○ 強制確定(マイナス) ■ 確定後にキャンセル ■ 一部だけキャンセル(年間払いを途中でキャンセルetc) シミュレーションテスト(E2Eテスト)
Slide 25
Slide 25 text
© 2012-2022 BASE, Inc. 25 #goconA @budougumi0617 ● デグレなしでエッジケース対応したい ● 決済レコードに状態を持たせたテストがしたい ○ オーソリ、確定、強制確定(マイナス)を処理した決済レコードのステータス ● 複数回リクエストを処理した結果なのでユニットテストでは検証できない ● 事前データを作る形式では”都合の良い”データになっている可能性 シミュレーションテスト(E2Eテスト)
Slide 26
Slide 26 text
© 2012-2022 BASE, Inc. 26 #goconA @budougumi0617 ● メルカリの柴田さんが書いていたE2Eフレームワークの簡易版 ○ https://engineering.mercari.com/blog/entry/gears-microservices/ ● 実DBに接続した本番同等のルーティングをhttptest.Serverで起動 ● 決済業者を模して残高確認webhookを複数回実行 ● DB上のレコードを検証 シミュレーションテスト(E2Eテスト)
Slide 27
Slide 27 text
© 2012-2022 BASE, Inc. 27 #goconA @budougumi0617 ● テストケースの実装など、メンテナビリティは低い ● 問題ない ○ ( 新 たなエッジケースの)テストケースが 追 加 されることはあっても、 既存のテストケースが変わることはない ○ 仕様が変わる時 == クレジットカードの決済の仕組みが変わるとき ○ 完全なブラックボックステストなので脆くない ● テストがあることで決済ロジックも安心して変更できる シミュレーションテスト(E2Eテスト)
Slide 28
Slide 28 text
© 2012-2022 BASE, Inc. 28 1 2 3 #goconA @budougumi0617 テストを書くことで自信をもって変化を積み上げる テストと設計は表裏一体 なぜテストするのか意識する まとめ
Slide 29
Slide 29 text
© 2012-2022 BASE, Inc. 29 © 2012-2022 BASE, Inc. 29 Tips
Slide 30
Slide 30 text
© 2012-2022 BASE, Inc. 30 #goconA @budougumi0617 ● go test shuffle=on ./… ○ 順不同に実行される ○ non-parallelなテーブルサブテストは逐次のまま ■ mapにするとよい ● go test -count=1 ./… ○ キャッシュを無視できる ○ 競合状態の再現のときなど ○ 明示的にキャシュを消すならば go clean -testcache Tips: よく使うgo testオプション
Slide 31
Slide 31 text
© 2012-2022 BASE, Inc. 31 #goconA @budougumi0617 ● go test -race ./… ○ 競合状態を発見してくれる ○ テスト実行中に"実際に起きない"と検知できない ○ CI上の実行ではひとまずつけておいてよい Tips: よく使うgo testオプション
Slide 32
Slide 32 text
© 2012-2022 BASE, Inc. 32 #goconA @budougumi0617 ● t.Helper() ● t.Cleanupが増えてすっきり書ける ● 共通化 ○ JSONファイル比較アサーション ○ 事前データの流し込み関数 Tips: ヘルパー
Slide 33
Slide 33 text
© 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:
Slide 34
Slide 34 text
© 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
Slide 35
Slide 35 text
© 2012-2022 BASE, Inc. 35 #goconA @budougumi0617 ● github.com/google/go-cmp ○ 構造体の比較をよしなにしたいとき ○ cmpoptsサブパッケージはちゃんと見ておいたほうがよい ○ SortMaps, IgnoreFields, IgnoreTypes etc… ● github.com/k1LoW/octocov ○ しゅっとカバレッジ可視化できる Tips: OSS
Slide 36
Slide 36 text
© 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