はじめに
Google Testの使い方の話は数多くあれど、じゃあ実際に効率的に使うにはどうしたらいいの?ということで、実際に仕事で使って編み出した使い方を紹介しようと思う。
なお、Google Mockを使うことと、テスト対象のソースがDIデザインとなっていることを前提とする。
Google Testを前提としないソースは、そもそもテストが難しく、効率的にテストを作ることはできない。
ソースの注意
まずはソースの実装側の注意。
気をつけるべきところは気を付けないと、テストを作る段階で割と困る。
何が何でもインターフェースクラスを作る
これくらいならいいかとインターフェースクラスを作らずにいると、途端にテストが大変になる。
というか、テストができなくて結局インターフェースクラスを作ることになる。
実装時点でテストを作ってれば、テストできなくてインターフェースクラスを作る羽目になるし、テストだけ誰かに任せたりしている場合は、テストをする人がインターフェースクラスを作る羽目になる。
出来る限り引数に右辺値参照を使用しない
これはGoogle Testの問題だが、Google Mockが右辺値参照に対応できていないので、右辺値参照の引数を持ったモックのメソッドを作れないためである。
とはいえ、使わざるを得ない場面は出てくるはずなので、その場合は自前で動作を変更できるよう、インターフェースを継承してstd::functionの処理を呼ぶだけのメソッドを作れば、手製モック処理としてなんとかできる。
ただ、数があるとしんどくなるので、あまり作らない方がいい。
そんなに大量に作ることもないとは思うけれども。
出来る限り機能を小さく持ったクラスを作成する
基本的なことだけれども、Google Testを使用する上では機能を小さく持ったクラスにすることでPrivateなメソッド、メンバ変数が減り、テストがしやすくなるというメリットがある。
Privateなメソッドやメンバ変数はテストでなんとかできないことはないのだけれど、面倒は少ないほうがテストは作りやすいし、効率も良くなる。
テスト全体
テスト全体でやるべきこと。
個別のテストを作る段階での話ではなく、プロジェクトをどういう構成で作るか考える段階で気にする必要がある。
ソースとテストは別プロジェクトにする
ソースの注意な気もするけども。
同じプロジェクトにしてテストソースを入れるより、プロジェクトを分けたほうが勝手が良い。
そもそも、何が実際のソースで何がテストなのか分かりにくくなるし、コンパイルオプション等もソース側にテスト用のものが混ぜ込まれ始めるので良くない。
テストプロジェクトからは、オブジェクトファイルやライブラリをリンクするだけなので、分けて困るようなこともない。
クラス単位でテストのソースファイルを分ける
クラスを小さな機能で分ければ、これくらいでいいはず。
メソッド毎では小さすぎるし、ファイル数が大変なことになる。
メソッド毎にファイルを分けてちょど良くなるような作りなら、そもそもメソッドの機能分割を考えた方がいい。
クラス単位で作って大きくなりすぎる場合も同様に、クラスの機能分割を考えた方がいい。
クラスのテスト
前述した「クラス単位でテストのソースファイルを分ける」で書いた通りに、クラス単位でファイルを分けていることを前提に、メソッドのテストを作るために必要なことを準備する。
全体の話ではないので、クラスのテストのソースファイル毎に用意する必要がある。
クラス全体のテストフィクスチャを作成する
あとからテストフィクスチャが必要そうだと用意するくらいなら、最初から用意しておいていい。
特にテストフィクスチャを使うことで不便することもない。
これはメソッドのテストを作るときに使うことになる。
注意ではないけれど、ここでSetUpとTearDownはクラスとしての処理ではほぼ使うことがないはずなので、特にoverrideする必要はない。
初期化や解放のテストもあるだろうし、SetUpとTearDownを使用すると正常系はいいが、異常系のテストに支障を来すので、別のクラスのインスタンスを作って使う羽目になる。
もし、テストで共通の処理が必要ならば、SetUpとTearDownは後述するメソッド毎のテストフィクスチャで実装すればいい。
テストフィクスチャにテスト対象のクラスのインスタンスを持たせる
これは共通で使うことになるものを、わざわざテスト毎に準備をするのは手間だからというだけ。
それぞれのテストで書くと可読性も悪くなる。
staticなメソッドしかないクラスの場合には必要ない。
テストフィクスチャをテスト対象のfriendにする
必要であれば。
Privateなメソッドやメンバ変数にアクセスする必要がある場合は、ここに書くのがいい。
後述するメソッド毎のテストフィクスチャをfriendにすると、friend指定がその都度増えることになるので、
その際、前述したクラスに直接アクセスするメソッドを作るようにすると楽ではあるが、他の実体が必要な場合に困るので、引数でクラスの実体を渡してアクセスするメソッドを作り、それをテストから呼び出して使うようにしたほうが使い勝手はいい。
メソッド毎のテスト作成
ここから各テストを作っていく際の話になる。
クラス全体のテストフィクスチャを継承したメソッド毎のテストフィクスチャを作成する
メソッド毎にテストフィクスチャを作成し、そのテストフィクスチャを使用してテストを作っていく。
そのままテストケース名に使用することになるので、テストフィクスチャと分かるような名前にするより、「クラス名_メソッド名」にすると何のテストか分かりやすくなる。
テスト名にはメソッドの各テスト内容にする。
正常系のテストのモック設定をメソッド毎のテストフィクスチャに作る
設定用のメソッドを作り、正常系用の設定をするようにしておく。
異常系はそこから必要な部分を変更するだけになるので、いろいろな設定をしなくて良くなるので効率的にテストを作れるし、テストに関係のない内容がテストメソッドに含まれなくなるので可読性が良くなる。
Setupはテストフィクスチャクラスに存在しているので、MockSetupとかがよい。
本当に確認したいモックのメソッド以外はEXPECT_CALLでAtLatest(0)にする
とにかくワーニングが出力されて結果が分かりにくくなるので、極力ワーニングが出ないようにした方がいい。
確認したいものだけ変更して確認できるようにすれば問題ない。
擬似的な動作には戻り値よりInvoke
無理に戻り値で設定するよりはInvokeで処理を書いたほうがやりやすい。
外に書くか中に書くかの差になるが、どこで何をするのかが分かりやすくなる。
Invokeの処理はメソッドを作るよりはラムダで書いたほうが作りやすい。
設定、実行、確認のシーケンスを守る
シーケンスを守ることでテスト内で何をするのか、何をテストしているのかが分かりやすくなるし、テストの書き方を迷わなくなる。
何をするかコメントを書いたメソッドをコピペしてもいい。
まとめ
Google Testの習熟度によりテスト作成の効率が変わるが、テスト作成のルール化を行うことで効率の平均化や、効率化を図ることができる。
あと、Google Testの使い方はあるけど、実際にどう使えばいいのかの話がないので、これが参考になれば幸いです。