Slide 1

Slide 1 text

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Unityテスト活動のふりかえり いも @adarapata 2021/12/12【年末だよ】Unity お・と・なのLT大会 2021 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1

Slide 2

Slide 2 text

自己紹介 いもです @adarapata 仕事でゲームとか作ってる DIとかテストが好きです 御朱印巡りはじめました https://adarapata.com 2

Slide 3

Slide 3 text

Unityテスト活動のふりかえり 公私で行ってきたUnityでのテスト関連について雑にふりかえる 実践編 自身がテストを書いていく上で意識しながら行ったことの話 啓蒙編 他者にテストを書いてもらう上で意識しながら行ったことの話 3

Slide 4

Slide 4 text

実践編 今回は以下の例での話をします スマートフォンゲーム開発 複数人で開発している 数年運用する想定である 一般的にアウトゲームと呼ばれる部分を中心とする E2Eの話はしません 4

Slide 5

Slide 5 text

面倒なところを先回りする 後々テスト書くときに面倒になりそうだな~という部分を先に対応す る シングルトンの撤廃 テスト領域を明確にする 通信は手厚くする データを用意しやすくする 5

Slide 6

Slide 6 text

シングルトンの撤廃 みんな大好きシングルトン staticなインスタンスは交換しにくい MonoBehaviourのシングルトンはシングルトンじゃない説 大体テスト書こうとしたときにシングルトンは障害となりやすいの で、最初から利用しない方針にする。 6

Slide 7

Slide 7 text

対応例 そもそもシングルトンインスタンスを作らなくていいような仕組み が必要 DIできたら基本的に必要なくなりそう VContainerを導入 https://vcontainer.hadashikick.jp 今回欲しいのはDIコンテナのみなのでVContainerは丁度よい 7

Slide 8

Slide 8 text

テスト領域を明確にする 全部書くのは大変で旨味が少ない MonoBehaviourが絡むとテストしにくい できないことはない プロダクトがカバーしたい範囲はどこなのかを洗い出す 8

Slide 9

Slide 9 text

対応例 今回は画面に対してModel-View-Presenter(MVP)構造 Presenter-Viewは書かない ViewはMonoBehaviourとして持っているので書くのが大変 PresenterはViewを直接持つので実質MonoBehaviourへの依存 上記は伝搬と出力くらいのコードにしましょう それ以外のクラスは書けるようにしていきましょう 方針を定めることで、テストできる領域にコードを寄せていく。 refs 「HumbleObjectPatternな話」 https://speakerdeck.com/adarapata/humble-object-patternnahua 9

Slide 10

Slide 10 text

通信を手厚くする アウトゲームの中枢に入ってくるやつ 外部との通信が入るとテスト難易度は高い テストを書かなかったとしても、開発中の通信偽装は欲しい サーバーができていないくても進めれる仕組みは欲しい この辺は実装前からオブジェクトを差し替えられる前提で作っておい たほうが楽 10

Slide 11

Slide 11 text

対応 通信クラスを抽象化してモックできるようにする モック用通信クラスを作る 一例 https://speakerdeck.com/adarapata/unityyong- mockclientfalsehua レスポンスを外部から流し込める機能があればとりあえずOK UnitTest用に使ってたが、アプリケーション側でそのまま使えそうだ ったので転用できた。 11

Slide 12

Slide 12 text

データを用意しやすくする テスト対象が依存しているデータをサクッと準備したい 読み込み方、更新方法が特殊だととてもテストしにくい 必ずサーバーから取るとか 巨大なDataManagerを持つと更に辛い Characterデータだけ欲しいのにShopデータを用意するのは嫌 ゲームで都度外部IOは体験が悪いので基本はオンメモリにしたい 上記の理由から、シンプルに1つの種類のデータを保持するクラスだ けあれば嬉しそう。 12

Slide 13

Slide 13 text

対応例 データの取得・更新はRepositoryを用意するという方針を取った public interface IUserCharacterRepository { void Update(List characters); UserCharacter Get(UserCharacterId id); } public interface IMasterCharacterRepository { MasterCharacter Get(MasterCharacterId id); } 13

Slide 14

Slide 14 text

外からの流し込みをしやすくするために保存・取得以外行わない 1つのRepositoryは極力1データクラスのみ取り扱う Repositoryは他のRepositoryを見ない ここを許容すると循環参照生まれがち 複数のRepositoryを取り扱いたいなら素直に上位のクラスを作る public class CharacterFinder { private readonly IUserCharacterRepository _userRepository; private readonly IMasterCharacterRepository _masterRepository; public (MasterCharacter, UserCharacter) FindCharacter(UserCharacterId id) {} } 14

Slide 15

Slide 15 text

データ生成はテスト用のFactoryメソッドを用意してあげる public class TestUtility { public static UserCharacter CreateCharacter(int id = 1, int masterId = 100) {} } ScriptableObjectなどの外部データ化もあり 15

Slide 16

Slide 16 text

リファクタし続ける テストはコード品質を上げない リファクタにこそ価値がある 開発中の「本当はこうした方がよい」は山ほどある 大体後からやるとつらい 時には影響範囲の大きいドラスティックな改修もやる必要がある。 その際にテストコードを指標にしてやるべきことを定める 16

Slide 17

Slide 17 text

体験例:通信の基盤の改善 通信クラスのレスポンスの構造を変えたいが、既にかなり利用されて いるので多くの箇所に影響してしまう。 以下の手順で改修した 1. おもむろに実装を変える 2. テストが大量に落ちる(ここで影響範囲がある程度見える) 3. 全て [Ignore] を書いてスキップさせる 4. 1個ずつ直してPR出していく ゴールが見えるようになることで、見積もりの精度と勇気をもらう 17

Slide 18

Slide 18 text

テストもリファクタし続ける テストコードもコードでありリファクタの対象 毎回書いてるコードは纏めていく テストを書くことを億劫にさせない 不要となったテストケースは削除していく 仕様が変わったけどなんとなく生きてるやつとか 頻繁に落ちて直してるケースなど 18

Slide 19

Slide 19 text

良かった所 基本的にはテストを書けるという環境を用意できた 大きな変更に対して強い意志を持ててる 改善できそうな所 未だにMonoBehaviourのテストの答えが出ない ユニットテストを書きたかったらUnityが提供するコンポーネント から距離を置くという判断になったが、それが適切であるか Prefabに対してのテストという観点を設けていいかもしれない Addressable使ってるなら読み込みは楽だしいけるかも 19

Slide 20

Slide 20 text

啓蒙編 プライベートで他の人がUnityでテストを書けるようになるための活 動を行っていました テストコードとTDDの座学 TDDリポジトリを使ったハンズオン ペアプロ・モブプロでのTDD 20

Slide 21

Slide 21 text

なぜやってるのか(抜粋) https://adarapata.hatenablog.com/entry/2021/09/15/233534 ゲーム開発はソフトウェア開発の中でもかなり複雑性が高いと感 じています。そして性質上変更頻度も高い。ゲームごとに作り方 が変わる以上これは仕方ないことだと考えています。だからこそ より変化に対応できるようにテスタビリティの高いコードになっ ていてほしいと考えていますがその難易度、そしてそもそものテ ストコード文化というものがないためにテストコードが書かれて いることは少ないです。 まずはUnity界隈でテストコードを書 く、という文化を作っていきたい。そんな気持ちで活動を行って います。 “ “ 21

Slide 22

Slide 22 text

TDDリポジトリを使ったハンズオン https://github.com/adarapata/UnityTDD_Example ハンズオン用に作ったRepo サンプルは業務で実体験したものを抽象化したもの Unityゲーム開発者ギルド(UGDG)や副業などで使いました ご自由にお使いください 22

Slide 23

Slide 23 text

ペアプロ・モブプロでのTDD 前述のハンズオンをモブプロしたり 特定の業務コードに対してペア・モブしたり 狙いとしては、テストに対して個人ではなくチームで関心を持って もらうこと どうテストを書いたらいいかを話し合える土壌づくり https://speakerdeck.com/adarapata/wakatutaqi- ninarumobupuroguramingu 23

Slide 24

Slide 24 text

活動の所感 テストを書く際に次のような課題が発生するケースが多かった 即物的な誘惑 レガシーに対しての踏み出し方 24

Slide 25

Slide 25 text

即物的な誘惑 1+1=2のテストを書いても仕方がない 即役に立つようなテストを書きたい 外部品質に直結する粒度で書きたくなる こうなるとテストの粒度が大きくなり、難易度も上がってしまう。 25

Slide 26

Slide 26 text

対策 バグを減らすという直接的な目的に囚われがち ユニットテスト ≠ バグを減らすではないことの徹底 アジャイルテスト4象限のお話 26

Slide 27

Slide 27 text

レガシーに対しての踏み出し方 すでにリリースされたタイトルでテストコードを書き始めるのは大変 往々にしてテスタビリティが低い 開発を止めることはできない ガッツリテストコードを書く時間を用意しにくい リファクタリングも同様 日々の開発に少しずつテストを書くという習慣をつけていく必要があ る 27

Slide 28

Slide 28 text

学習のためのテスト戦略 なにはともあれ書けそうなものを探してみる。 staticメソッドのテスト staticメソッドは状態を持たない AのときにBであるというシンプルなテストを書きやすい ここから初めてテストを書くという感覚を身に付けていく ただしシングルトンは除く 28

Slide 29

Slide 29 text

学習のためのテスト戦略 書けない!と思ったときに問題を分析していく 何をテストしていいのかわからないから 目の前のクラスの役割は理解できているか? テストするための依存オブジェクトが準備できないから クラスが責務を負いすぎていないか? テストの記法がわからないから みんなでNUnitのページ見ましょう 29

Slide 30

Slide 30 text

大きなコードとの向き合い方 例:「HP10%以下で強力な攻撃を放つ敵」という巨大なクラスの場合 public class BigEnemy { public void Update() { /* すごく長い処理がいっぱい */ if(life / maxLife < 0.1F){ StrongAttack(); // 攻撃ダメージ計算とか演出とか } } } 30

Slide 31

Slide 31 text

全てを解決しようとすると難しい HP10%以下になったらちゃんと動く? 攻撃処理は想定通り動く? そもそもEnemyは単体で動くの? 様々な問題と向き合うことになり、頭が痛くなる・・。 31

Slide 32

Slide 32 text

問題の分割統治 「HPがn%以下になったことを検知できる機能」にフォーカスを当 てることはできる そこだけクラスを切り出し、テストを書く 動作を保証できたら該当箇所を差し替える 大きなコードを小さな機能に分解していき各個撃破で問題を潰してい く 32

Slide 33

Slide 33 text

良かった所 少なからずTDDへの興味・理解を示す人がいた ライブラリを理解するためにテスト書いたり まったくわからない状態から「書いてみる」に進められた 改善できそうな所 テストコードの書き方とTDDを同時に教えていた テストコード自体も初めての人が多かったので 場合によっては初モブプロも同時に教えることになった 学習難易度が上がり、全員が理解できたとは言えない状況だった。 33

Slide 34

Slide 34 text

最後に テストはゲームを面白くしないが、もっと面白くできると思ったとき に実現するための支えとなる。 34

Slide 35

Slide 35 text

引用 P27. Agile Testing Quadrants https://www.informit.com/articles/article.aspx? p=2253544&ranMID=24808 35