Slide 1

Slide 1 text

『呪術廻戦 ファントムパレード』の バックエンドにおけるテスト実装の取り組み 1 サーバーサイドエンジニア / 呪術廻戦 ファントムパレード 守田 一喜 Morita Kazuki

Slide 2

Slide 2 text

『呪術廻戦 ファントムパレード』とは 2

Slide 3

Slide 3 text

3 『呪術廻戦 ファントムパレードは、 2018年より「週刊少年ジャンプ」 (集英社)にて連載中の 芥見下々(あくたみげげ)氏による 人気漫画を原作としたTVアニメ 『呪術廻戦』を元にした、作品初の スマートフォンゲームです。 TVアニメ『呪術廻戦』の第1期の物語を 追体験できるだけでなく、福岡を舞台にした 『ファンパレ』オリジナルの ストーリーが楽しめる コマンドバトルRPGとなっています。

Slide 4

Slide 4 text

テスト実装について 4

Slide 5

Slide 5 text

バックエンドにおけるテスト実装の役割 5 テストを通して、APIや実装の振る舞いが正しい事を動作確認する レベルアップするリクエスト - キャラのデータ - 消費するアイテムの データ - 何レベル強化するか API レベルアップしたレスポンス - レベルアップした キャラのデータ - 所持数の減った アイテムのデータ テスト用に仮のデータを作って、実行してみる

Slide 6

Slide 6 text

バックエンドにおけるテスト実装の役割 6 テストを通して、APIや実装の振る舞いが正しい事を動作確認する レベルアップするリクエスト - キャラのデータ - 消費するアイテムの データ - 何レベル強化するか API レベルアップしたレスポンス - レベルアップしてない キャラのデータ - 所持数が減ってない アイテムのデータ 想定外の結果となったら、テスト失敗

Slide 7

Slide 7 text

バックエンドにおけるテスト実装の役割 7 テストを通して、APIや実装の振る舞いが正しい事を動作確認する レベルアップメソッドの引数 - キャラのデータ - 何レベル強化するか メ ソ ッ ド レベルアップメソッドの戻り値 - レベルアップした キャラのデータ APIだけでなく、メソッド単位でも実装する

Slide 8

Slide 8 text

これからお話しする事 8 テスト実装の取り組みをご紹介します - テスト実装の取り組み - テスト実装の現状 - テストの活用事例紹介

Slide 9

Slide 9 text

前提として 9 ファンパレのバックエンドで使用している技術 - 言語: PHP - Webフレームワーク: Laravel - テストフレームワーク: PHPUnit

Slide 10

Slide 10 text

前提として 10 アーキテクチャはADR+Repositoryパターンを採用

Slide 11

Slide 11 text

テスト実装の取り組み 11

Slide 12

Slide 12 text

テスト実装の取り組み 12 主に3種類のテストを実装しています - Unitテスト - Featureテスト - Integrationテスト 以下の事を重視しています - なるべく全てのメソッドを網羅するテストを書くこと - 不要なDBアクセスを避け、テストを軽くすること

Slide 13

Slide 13 text

Unitテストとは 13 いわゆる単体テストで、 Service系とRepositoryに対してそれぞれ実装

Slide 14

Slide 14 text

Unitテスト実装時のルール 14 「テストが仕様書といっても過言ではない」 - 原則1メソッド1テスト - 異常系のテストも書くこと - Repositoryのテストはデータストアへのアクセスを行う - Serviceのテストはデータストアへのアクセスを行なってはいけない - Mockを利用する

Slide 15

Slide 15 text

Featureテストとは 15 1つのHTTPリクエスト単位の動きをテストする

Slide 16

Slide 16 text

Featureテストの役割 16 主にリクエストのバリデーションが正しいかを検証 レベルアップするリクエスト - キャラAのデータ - 強化アイテム15個 - 「3レベル強化」 チェック項目 ● キャラのデータは正しい? ● アイテムの消費数は整数? ● 強化するレベルは正の整数? テスト用に仮のデータを作って、実行してみる Request

Slide 17

Slide 17 text

Featureテストの役割 17 主にリクエストのバリデーションが正しいかを検証 レベルアップするリクエスト - キャラAのデータ - 強化アイテム15個 - 「-5 レベル強化」 チェック項目 ● キャラのデータは正しい? ● アイテムの消費数は整数? ● 強化するレベルは 正の整数ではない 異常なリクエストが、正しくエラーになることを確認 Request

Slide 18

Slide 18 text

Featureテスト実装時のルール 18 - DBの書き込みをしないため、Serviceをmockして扱う - レスポンスは 200 OK かどうかを見る - 実際の返り値はUnitTestで保障されているため 値が正しい事の検証はしない

Slide 19

Slide 19 text

Integrationテストとは 19 結合テストの事 DBにデータを入れてAPI丸ごとの実行をテストする

Slide 20

Slide 20 text

Integrationテスト実装時のルール 20 - Actionごと(APIごと)に必ず書く - mockを使わず、包括的にテストを実施する - 実際にDBにデータを入れてテストする - HTTPステータスではなく、実際のResponseの内容を見て評価する - 正常系のみのテストが好ましい - 1テストごとにデータ挿入、データ削除をしているので truncateする分大変に重い - なるべくテストを軽くするため、ケース数を減らす

Slide 21

Slide 21 text

その他テスト実装に関して 21 その他、以下のようなルールがあります - データプロバイダを使って、入力データのパターンを作ってテストする - factoryを使ってデータを用意する - テストは、マスタの実際のデータを利用する必要はない - なるべくテストを軽く保つこと - 不要なDBアクセスは避ける - テストケースも必要分のみで良い

Slide 22

Slide 22 text

テスト実装の取り組み - まとめ 22 主に3種類のテストを実装しています - Unitテスト - Featureテスト - Integrationテスト 以下の事を重視しています - なるべく全てのメソッドを網羅するテストを書くこと - 不要なDBアクセスを避け、テストを軽くすること

Slide 23

Slide 23 text

テスト実装の現状 23

Slide 24

Slide 24 text

テスト実装の現状 24 ファイル数は以下の通り - Unitテスト 約1150ファイル - Featureテスト 約160ファイル - Integrationテスト 約220ファイル テストケースは7500ケース以上

Slide 25

Slide 25 text

テスト実行にかかる時間 25 テストの実行に掛かる時間は以下の通り - 12分

Slide 26

Slide 26 text

テスト実装にかかる時間 26 話者が3種の実装中でテスト実装に掛かった時間は以下の通り - 規模の大きなAPIの実装 約50分 (実装全体: 約240分) - やや規模の大きなAPI改修 約15分 (改修全体: 約120分) - 小規模な改修 約10分 (改修全体: 約40分) 特に複雑で規模の大きな新規実装においては、 テストの実装はかなり時間がかかってしまう。

Slide 27

Slide 27 text

テストに対するチーム内の意識 27 「テスト実装は、当たり前」の文化が根付いている - サーバーリードエンジニアが強い意思を持っている - テスト実装が欠けているとレビューが通らない チームメンバーが、テスト実装に対して肯定的な考え - チーム発足当初は、手厚いテスト実装に否定的だったメンバーが 現在は肯定的な考えに変わっている例も

Slide 28

Slide 28 text

チームメンバーの思うテストのメリット 28 安心できる/実装を保証できる テストを意識して設計・実装するので、良いコードになりやすい気がする (何をするメソッドか明確な設計・実装になる) 実装中の動作確認がしやすい リファクタがやりやすい 仕様変更によりコードを変更した場合の対応漏れなどに気づける

Slide 29

Slide 29 text

チームメンバーの思うテストのデメリット 29 テスト実装に時間がかかる 書き方に悩むことがある(手が止まる人がたまにいる気がする) どうやってテストすれば良いか分からないケースに時間がかかったりする テストが増えてきて長くなると作業効率が落ちる CIが完了するのを待ったり

Slide 30

Slide 30 text

テストの活用事例 30

Slide 31

Slide 31 text

テストの活用事例について 31 N+1問題の検出する仕組み - Integrationテストを活用 - 実際にDBにデータを挿入、選択するテスト - テスト中のクエリを分析する テストを活用した仕組みを1つご紹介します

Slide 32

Slide 32 text

N+1問題とは 32 データの取得時に、大量のクエリを発行してしまう問題 実装の不備によって引き起こされる 例えばフレンドの情報を取りたい場合 - フレンドの一覧をリストで取得する - フレンドのプロフィールをリストをループして取得する と実装してしまうと、フレンドの人数分クエリを発行してしまう リスト取得で1回、フレンドN人のプロフィール取得にN回 合計N+1回のクエリが発行されるので、N+1問題と呼ばれる

Slide 33

Slide 33 text

N+1問題とは 33 問題の本質は、同じクエリを何度も発行してしまう事 例えばフレンドの情報を取りたい場合 - フレンドの一覧をリストで取得する - フレンドのプロフィールをリストをループして取得する この例では「フレンドのプロフィールを取得する」クエリを 何度も繰り返し実行してしまう 本来は、全てのフレンドのプロフィールを纏めて一度に取得すべき

Slide 34

Slide 34 text

N+1問題の検出 34 Integrationテストをすべて実行していく テストケースごとにDBに吐かれたクエリを集計 LaravelではenableQueryLogを使用すると、 クエリログを有効化できる

Slide 35

Slide 35 text

N+1問題の検出 35 クエリを纏めて、同じクエリの重複を検知 これにより、Prepare文の重複が検出される

Slide 36

Slide 36 text

N+1問題の検出 36 毎週水曜日に、Slackに検出されたテストを通知

Slide 37

Slide 37 text

N+1問題の検出 37 あくまでも、疑わしい箇所の検出 適切な箇所についても検出される場合がある リリース前の、怪しい箇所の洗い出しに役に立った

Slide 38

Slide 38 text

38 ご清聴ありがとうございました ©芥見下々/集英社・呪術廻戦製作委員会  ©Sumzap, Inc./TOHO CO., LTD.