テスト駆動開発 イメージ

XFLAG Tech Noteに学ぶ、Unity テスト駆動開発

こんにちは! ねこです。

今回は、XFLAG Tech Noteに学ぶ、Unity テスト駆動開発。

ということで、XFLAG Tech Note の MVP アーキテクチャにのっとって、テスト駆動開発を行ってみましたので、その有用性を書いていきます。

Unity で MVP 設計を初めてやった人が、ついでにテストをやってみたら、どうなったのか?

それでは、ご覧ください。

ぜひ、前の記事もお読みください

まずは、こちらの記事を先に読んでいただくと、ご理解いただきやすいです。

関連記事

こんにちは! ねこです。最近、エンジニアの仕事で、技術検証を兼ねた Unity で制作しているゲームのアーキテクチャ設計をしています。私は Unity アプリのフロントエンジニア経験は、もう 4 〜 5 年くらいかなと思い[…]

MVP 設計 方法

現在、「学ぶは、まねぶ」の精神のもと、XFLAG Tech Note に書かれてある内容を、可能な限り再現しています。

今回の記事は、その続きとして、テスト駆動開発について書いていきたいと思います。

XFLAG Tech Note とは?

モンストの開発・運用元で有名な XFLAG さんが、技術書だけの同人イベント「技術辞典5」で出展していたものです。

現在は、無料で公開されており、誰でも読むことができるようになっています。

XFLAG × Career

技術書オンリーイベント『技術書典5』にXFLAG(エックスフラッグ)として初めて参加した様子のレポートです。XFLAGの…

今回も、その中の、第 3 章「とある Unity 開発事例」を参考に書かせていただきます。

テスト駆動開発を行おう

テスト駆動開発をするにあたって、やはり、内容をある程度理解しておく必要があります。

テスト駆動開発とは

テスト駆動開発をざっくりと説明しますと、プログラム開発手法の一種です。

進め方を簡単に説明します。

動くプログラムを書く前に、先に要件をテスト&チェックするコードを書きます。

もちろん、そのままだと、テスト結果は、失敗(レッド)になります。

これを、いったんテストがとおるコード(グリーン)を書き、エラーをなくします。

そして、後日ちゃんと作り込み(リファクタリング)を行うのです。

もし、テスト駆動開発の詳細を知りたい方は、こちらの記事が参考になります。

記事みた中で一番、平文で読みやすく、芯をくっているような気がします。

クラスメソッド発「やってみた」系技術メディア | Developers.IO

はじめに モバイルアプリサービス部の中安です。 このたび、テスト駆動開発(Test Driven Development…

ところで、なぜ、テスト駆動開発をする必要があるんでしょうか?

テスト駆動開発のメリット

テスト駆動開発のメリットを表すと、こんな感じだと思います。

  • ソフトウエアの品質を一定に担保できる
  • 開発後期の開発スピードの低下の軽減
  • 開発者の心理的負担の軽減

私が一番いいと思うのが、開発者の心理的な負担の軽減です。

少なくとも、レビュアーは、テストに書かれている要件については、全て通っているという前提で、レビューできます。

また、開発後期となると、不安になる修正の影響範囲とエンバグ問題。

動くけど、実装がめちゃくちゃなコードは、場合によっては、たった 1 行を変えるだけでも、緊張が走ります。

場合によっては、それだけで結構デバック工数も発生しますよね。

しかし、テストを書いていくことで、そこに書かれた機能要件は満たしていることになり、余計なチェックや心理的な負担も減ります。

また、テストがし辛いクラスや、テスト工数が多すぎるクラスは、設計上、何かしらの問題があります。

そこも、見える化されそうなのも、副次的なメリットかもしれません。

これは、コードを書く側も、見る側も幸せになれそうです。

Unity 開発でテストはいらない?

はい。私もそう思ってました。実際にテストを書くまでは。

私自身、実際に運用にはのせているわけではないので、今後進めるのであれば、様々な問題に対処する必要は出てくると思います。

しかし、それを差し置いても、テスト駆動開発の有用性は、ひしひしと感じました。

ちゃんと稼働すれば、チーム開発を行う上での、エンジニアの心理的な負担が、だいぶん減りそうです。

ビヘイビア(振る舞い)駆動開発と言うべき?

今まで、テスト駆動開発やったことなかった私です。

ですので、「テスト駆動開発」という言葉に、ぴんと来なかったんですよね。

でも、テスト駆動開発 ≒ ビヘイビア(振る舞い)駆動開発と聞いて、納得しました。

簡単に言うと、テストコードに書く内容に、要件にのっとった振る舞いを書く。ということです。

それなら、先に書いておくメリットがわかりやすく伝わります。

例えば、「先にテストを書く」、というより、「先に振る舞いを書く」。と、言った方がしっくり来ますよね。

実際、要件(振る舞い)を先に決めてしまう手法ですので、ビヘイビア(振る舞い)駆動開発というべきかもしれません。

依存性の注入(Dependency injection)

テスト稼働開発をするにあたって、疎結合を維持するために必要とされるテクニックである、依存性の注入。

こちらの説明をしていこうと思います。

ちなみに、XFLAG Tech Note で、Zenject の記載がありましたが、今回は説明しません。

Zenject とは依存性の注入を行いやすくするためのフレームワークです。

依存性の注入 = Zenject ではありませんので、ご注意下さい。

内容は、下で説明したものを、より簡単に行えるものだと思って頂ければと思います。

ずっと疑問だった、この書き方

XFLAG Tech Note の MVP アーキテクチャを、マネするにあたって、メリットのわからなかったこの書き方。

public class SampleRepository : ISampleRepository{ private SampleEntity entity; public SampleRepository (ILoader loader) // ← ここの部分 {  entity = loader.Load<SampleEntity>(); }}public class SamplePresenter : MonoBehaviour{ public void Initialize (IApiClient client, ISampleRepository repository) // ← ここの部分 { }}

なぜ、わざわざ、外側から必要データを渡す必要があるのか?

元の記事では、なぜ、そのようにしているのかが、はっきりと書かれて無かったため、意図を汲み取れてませんでした。

しかし、これこそが、テスト駆動開発に重要な役割をしめしているのです。

依存性の注入(Dependency injection : DI)とは

上記にあるような、クラスに必要なパーツ(クラス)を外部から取得するテクニック。

それを、依存性の注入(Dependency injection、略して DI )といいます。

依存性を注入するという、非合法の香りただようネームですが、れっきとしたソフトウエアの設計(デザイン)パターンです。

ちなみに、Dependency = 依存性 という名前ですが、もとの意味としては、「オブジェクト」に近いです。

ですので、じっさいには「依存オブジェクトの注入」というほうが、より内容を表していると思います。

意味としては、そのクラスの成り立ちに必要で、依存しているオブジェクトを、外部から注入するソフトウエアパターンのことです。

依存性の注入(DI)を行うと、以下のメリットがあります。

  • 結合度の低下によるコンポーネント化の促進
  • 単体テストの効率化
  • 特定のフレームワークへの依存度低下

要するに、オブジェクト同士のつながりが薄く(疎結合に)なるから、パーツ単位で取り出しやすいし、変化にも対応しやすくなるよね。ということです。

テストを書いたら、書きやすい

なぜ、依存性の注入(DI)を行うのか?

それは、テストコードを書いて分かりました。

すごく、テストが書きやすいのです。

例えば、このクラスの GetName ()をテストしたいと思います。

// SampleRepository.cs ※テストしたいクラスpublic class SampleRepository{ private SampleEntity entity; public SampleRepository (ILoader loader) {  entity = loader.Load<SampleEntity>(); } public string GetName () {  return "名前は、" + entity.name + "です。"; }}

上記のコードを、テストするサンプルコードがこちらです。

// TestLoader.cs ※デバッグ用 Loader クラスpublic class TestLoader : LoaderBase, ILoader{ public TestLoader () {  SampleEntity sampleEntity = new SampleEntity();  sampleEntity.name = "サンプル名";  // オブジェクトを追加  base.AddObject(sampleEntity); }}// SampleRepositoryTest.cs ※SampleRepository を、テストするクラスpublic class SampleRepositoryTest{ SampleRepository repository; [SetUp] public void InitializeAllTest () {  var loader = new TestLoader ();  repository = new SampleRepository (loader); } [Test] public void 名前チェック () {  Assert.AreEqual (repository.GetName (), "名前は、サンプル名です。"); }}

初期化も簡単、テストも簡単なのが、理解できると思います。

しかし、例えば、このクラスをテストしようと思ったらどうでしょうか?

// SampleBadRepository.cs ※テストしたくても出来ないクラスpublic class SampleBadRepository{ private SampleBadEntity entity; public SampleBadRepository () {  entity = new SampleBadEntity ();  entity.name = "サンプル名" + Random.Range (0, 100).ToString (); } public string GetName () {  return "名前は、" + entity.name + "です。"; }}

この、GetName () をテストする場合、受け取るのは、中で隠された文字列になってしまいますので、特定の文字で比較ができません。

Assert.AreEqual (repository.GetName (), );

ここから、XFLAG Tech Note に示された書き方だと、なぜテストが書きやすくなったのか、説明していきたいと思います。

なぜテストが書きやすかったのか

なぜ、テストが書きやすかったのか?

それは、当たり前ですが、そのクラスが必要なパーツを、外部からカスタマイズできるようにしたからです。

SampleEntity を外部から変更できるため、名前の変更ができた。

そのため、データの変更を外部から行うことが可能になっています。

もうひとつは、参照元を上位クラス(インターフェース)から行っていることです。

こちらのテストの部分に絞ってクラスの参照を書くと、このようになります。

共通の ILoader インターフェースで、やりとりしている。

見ると、共通のインターフェース(ベースクラス)を経由してやりとりしているのが分かりますね。

このように抽象クラス参照しあうのには、メリットがあります。

それは、実体クラスを簡単に変更できることです。

今回の場合は、注入するデータをテスト用データに置き換えました。

だから、テストコードを書くときに、簡単に記述できたのです。

テストの場合は、テスト用のLoader クラスからデータ受け取り。

テストが書きやすい = 設計がしっかりしている

テストコードを書いてみて思ったのは、テストが書きやすいコードは、設計がしっかりしているとも取れるということ。

もし、仮にテストが書きづらいものが出てきた場合、設計そのものを見直すべきという、注意サインになるかも知れません。

例えば、テストを書いていて、このようなことが起こった場合。

  • 初期化がしにくい … 依存している外部クラスが多い可能性
  • テスト項目が多い … クラスに複数の機能を詰め込み過ぎている可能性
  • テストが書けない … 依存性のあるクラスが隠蔽されている可能性

これらのケースが出てきたら、メンバーと相談するのが良さそうですね。

テスト駆動開発の準備

では、実際にテスト駆動開発をやりたい場合、どうすればいいか。

実際のツールを確認しつつ、見てみましょう。

Test Runner を使おう

Test Runner は Unity 標準のテスト実行環境です。

2019.2 から、Package 化されています。

実際にテストしてみよう

実際にテスト駆動開発を始めてみようとした場合ですが、色々な準備が必要です。

前提として、テストを行う環境を作るのに、Assembly Definition Files の設定をする必要があります。

特に、リリース済みのプロジェクトに関しての Assembly Definition Files 設定の追加は、ほとんどの場合、アセットバンドルの再構築が必要になると思いますので、ご注意。

こちらのサイトは、初期設定のやり方から、詳しく記載されていますので、かなり参考になります。

Qiita

# 実は既にもうそんな時代なのかも...レガシーコードとはテストのないコードのことであるテスト駆動開発 …

最後に

もし、XFLAG の資料で設計を行うのであれば、やはり、テスト駆動開発は導入するべきだと思います。

やはりテストも含めての事例であります。

今後同じようにマネされる方がいらっしゃいましたら、これが少しでも参加になれば幸いです。

テスト駆動開発 イメージ
最新情報をチェックしよう!