はじめに
Visual StudioのGoogle Testプロジェクトを使用したテストの作成を行います。
Google Testの使い方より、プロジェクトの作成とVisual Studioでの使い方に重点を置きます。
環境
OS:Windows10
開発環境:Visual Studio 2019
Google Test:v1.4.0(NuGetで導入)
テスト対象のプロジェクト作成
まずは通常のC++のプロジェクトを作ります。
実行した内容をすぐ見れるように、今回はコンソールプロジェクトにします。
ソリューショ名:GoogleTestSample
プロジェクト名:TargetProject
あとでテスト用のプロジェクトを追加するので、「ソリューションとプロジェクトを同じディレクトリに配置する」のチェックは外した状態にしておきます。
チェックが外れているのを確認したら、作成ボタンを押してプロジェクトを作成します。
テスト対象の処理作成
テストを作る前にテストを行う処理を作っていきます。
関数でもクラスでも問題ないですが、クラスを作って、その中のメソッドをテストするようにします。
関数のテストをしたい場合でも、テストを作る際にやることは一緒です。
TargetClass.hとTargetClass.cppを追加して、TargetClassを作ります。
テスト用にpublicとprivateに、それぞれPublicMethodとPrivateMethodというメソッドを追加します。
メソッドの中身は簡単に、入力された値が0以上ならtrueを返し、それ以外はfalseを返すメソッドと、入力された値が0以下ならtrueを返し、それ以外はfalseを返すメソッドにします。
#pragma once
class TargetClass
{
public:
bool PublicMethod(int input);
private:
bool PrivateMethod(int input);
};
#include "TargetClass.h"
bool TargetClass::PublicMethod(int input)
{
if (input >= 0)
{
return true;
}
return false;
}
bool TargetClass::PrivateMethod(int input)
{
if (input <= 0)
{
return true;
}
return false;
}
適当にmain関数で呼び出しておきます。
#include <iostream>
#include "TargetClass.h"
int main()
{
TargetClass target;
std::cout << target.PublicMethod(1) << std::endl;
}
Google Testプロジェクトの作成
ソリューションに新しいプロジェクトとして追加していきます。
ソリューションを右クリックして、追加 -> 新しいプロジェクト を選びます。
Google Testのプロジェクトを選択してプロジェクトを作ります。
プロジェクト名はTargetProjectTestにします。
プロジェクト名を入力して作成ボタンを押すと、「テスト プロジェクトの構成」のダイアログが出てきます。
テストするプロジェクトの選択に、先程作った「TargetProject」を指定します。
その下は、特に理由がないのであれば、そのままスタティックライブラリと動的リンクを選択しておきます。
プロジェクトを作成すると、デフォルトのテストが入ったプロジェクトが作成されます。
ここにテストを追加していきます。
publicなメソッドのテストの追加
Google TestのプロジェクトからTargetProjectを参照した状態になっているので、ヘッダーをincludeして呼び出せばOKです。
ただし、パスが通っていないので、TargetProjectTestプロジェクトから参照するようにパスを入れる必要があります。
テストするモジュールが増えた時に手間になるので、追加のインクルードディレクトリにパスを入れるようにしたほうが楽です。
TargetProjectTestプロジェクトのプロパティを開いて、「構成プロパティ -> C/C++ -> 全般 -> 追加のインクルードディレクトリ」に「\$(SolutionDir)TargetProject\」を追加します。
テストはPublicMethodがture/falseを返すそれぞれの条件でのテストを作ります。
#include "pch.h"
#include "TargetClass.h"
TEST(TargetClassTest, PublicMethodTest) {
TargetClass target;
EXPECT_EQ(true, target.PublicMethod(1));
EXPECT_EQ(false, target.PublicMethod(-1));
}
これでビルドを行い、TargetProjectTestで実行すれば、テストが実行されます。
「メニュー -> テスト -> すべてのテストを実行」からテストの一覧を表示して、テストを実行しましょう。
すべてのテストを実行が選択できない場合は、テストを認識されていないので、
「メニュー -> テスト -> テストエクスプローラー」からテストの一覧を表示して、テストを実行しましょう。
その前にビルドが通らないかと思いますが。
それもそのはず、ヘッダーを参照したところでクラスの実体を参照できていないので、TargetProjectのクラスが使えません。
実行ファイルを生成するようになっている以上、外部に公開されているのは実行ファイルだけです。
ということで、テストの対象を参照できるようにしていきます。
テスト対象のオブジェクトの参照の追加
テスト対象がライブラリになっているのであれば、ライブラリを参照すればいいので問題は起きないが、実行ファイルやDLLの場合は、公開されない部分を参照できずに難しくなる。
ここで参照の解決するための方法は2つ。
- TargetProjectTestにテストしたいソースコードを含める。
- テストするcppをインクルードする。
- objファイルを参照する。
1は簡単だけども元のプロジェクトの参照関係を全て持ってくる必要がある。
更に言うと、元のプロジェクトとは別でビルドされることになるので、元のプロジェクトの動作と同じだという保証がない。
2は参照ごとにソースファイルのビルドが行われる(つまり参照ごと全て実体としては別物)のと、ヘッダーから他のヘッダーのクラスを参照していた場合に、それぞれを参照できるように必要がある。
つまり、1と同様に元のプロジェクトの動作と同じだという保証がない。
ということで、3でやることにする。
TargetProjectTestプロジェクトのプロパティを開いて、「構成プロパティ -> リンカー -> 入力 -> 追加の依存ファイル」に以下を追加します。
Win32:「\$(SolutionDir)TargetProject\\$(Configuration)\TargetClass.obj」
x64:「\$(SolutionDir)TargetProject\\$(Platform)\\$(Configuration)\TargetClass.obj」
これでビルドを行うと、テストの実行ができるようになります。
テストを実行すると、全てのテストが成功していると思います。
privateなメソッドのテストの追加
やるな。
外部からアクセスできないメソッドに対して好きな値を与えてテストする意味はない。完全にない。
そんなテストが必要なのにprivateにする必要がある設計も存在しない。
しかし、世の中にはそんなことも分からない我儘な人がいるので一応やります。
やり方はいくつかありますが、今回はFRIEND_TESTマクロを使います。
TargetClassを書き換えて一部のフレンドだけがアクセスできるようにします。
とりあえず、GoogleTestのヘッダーを参照できるようにTargetProjectプロジェクトのプロパティを開いて、「構成プロパティ -> C/C++ -> 全般 -> 追加のインクルードディレクトリ」に「\$(SolutionDir)packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.4\build\native\include」を追加します。
マクロが必要なだけなので、ライブラリへのリンクは必要ないです。
次に、ヘッダーファイルの方を変更していきます。
#pragma once
#include "gtest/gtest_prod.h"
class TargetClass
{
public:
bool PublicMethod(int input);
private:
FRIEND_TEST(TargetClassTest, PrivateMethodTest);
bool PrivateMethod(int input);
};
テストの方も追加していきます。
テストはPublicMethodと同様、ture/falseを返すそれぞれの条件でのテストを作ります。
#include "pch.h"
#include "TargetClass.h"
TEST(TargetClassTest, PublicMethodTest) {
TargetClass target;
EXPECT_EQ(true, target.PublicMethod(1));
EXPECT_EQ(false, target.PublicMethod(-1));
}
TEST(TargetClassTest, PrivateMethodTest) {
TargetClass target;
EXPECT_EQ(false, target.PrivateMethod(1));
EXPECT_EQ(true, target.PrivateMethod(-1));
}
これでビルドしてテストを実行すれば、各テストが成功した状態になると思います。