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