【C++】UnitTest(C ++用Microsoft単体テストフレームワーク／vstest.Console.exe)の使い方概要

# UnitTest関連記事
https://tera1707.com/entry/2022/02/06/144447#UnitTest

# やりたいこと
C++で作成したDLLがエクスポートしている関数の単体テスト(UnitTest)を行いたい。

# 概要
VisualStudio(2017)で単体テストプロジェクトを作成し、そこで単体テストを行う。

# やること
### プロジェクト作成
DLL作成用のプロジェクトを含むソリューションに、単体テスト用プロジェクトを追加する。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/352601/f380cbc9-69dc-6c94-e8f3-4d39f4eb78cb.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/352601/82d987e9-6796-c335-1701-68ef7ef44838.png)

### テスト対象のdllの必要物を参照設定する

[C++で作成したDLLをC++で呼ぶ](https://qiita.com/tera1707/items/56849d0eac773e24c4f8)でやったように、テストしたいDLLを使えるようにする。
(.libが見えるようにリンカの設定をし、DllImportをするための.hが見えるようにインクルードフォルダの設定をする)

### 単体テスト用コードを作成
初期状態で、下記のようなコードが作成される。

``` unittest1.cpp
#include "stdafx.h"
#include "CppUnitTest.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace UnitTest1
{		
	TEST_CLASS(UnitTest1)
	{
	public:
		
		TEST_METHOD(TestMethod1)
		{
			// TODO: テスト コードをここに挿入します
		}

	};
}
```

ここに、以前作成したDLLを呼ぶコードを追加する。
([もとのDLLはこちらを参照](https://qiita.com/tera1707/items/56849d0eac773e24c4f8))※単体テスト用に関数をいくつか追加。

---

DLLの関数を、下記のようにテストコードで呼び出す。

``` unittest1.cpp
#include "stdafx.h"
#include "CppUnitTest.h"

#include "DllTest.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace UnitTestCpp
{		
	TEST_CLASS(UnitTest1)
	{
	public:

		BEGIN_TEST_METHOD_ATTRIBUTE(TestMethod_Normal)
			TEST_PRIORITY(1)
		END_TEST_METHOD_ATTRIBUTE()

		TEST_METHOD(TestMethod_Normal)
		{
			wchar_t* buf_in = L"あいうえお";
			wchar_t* buf_out = nullptr;

			int ans1 = Test_MyApiAdd(1, 2);
			int ans2 = Test_MyApiSub(3, 1);
			Test_MyApiPointerCopy(buf_in, &buf_out);

			Assert::AreEqual(ans1, 1 + 2); // 正常に終了
			Assert::AreEqual(ans2, 3 - 1); // 正常に終了
			Assert::IsNotNull(buf_out);    // 正常に終了

			Logger::WriteMessage("Test OK");// デバッグ時のログ(出力欄)に出力
		}

		BEGIN_TEST_METHOD_ATTRIBUTE(TestMethod_Error)
			TEST_PRIORITY(1)
		END_TEST_METHOD_ATTRIBUTE()

		TEST_METHOD(TestMethod_Error)
		{
			wchar_t* buf_in = nullptr;
			wchar_t* buf_out = nullptr;

			int ans1 = Test_MyApiAdd(1, 2);
			int ans2 = Test_MyApiSub(3, 1);
			Test_MyApiPointerCopy(buf_in, &buf_out);

			Assert::AreEqual(ans1, 1 + 5);    // ans1と1+5が一致しないので、ここでTestストップ。
			Assert::AreEqual(ans2, 100 - 1);  // 実施されない
			Assert::IsNotNull(buf_out);       // 実施されない

			Logger::WriteMessage("Test OK");
		}
	};
}

```

DLLの関数のコードは今回はこんな感じ。
足し算、引き算、文字列へのポインタをコピーする、だけの関数。

``` DllTest.cpp

int __cdecl Test_MyApiAdd(int p1, int p2)
{
	return p1 + p2;
}

int __cdecl Test_MyApiSub(int p1, int p2)
{
	return p1 - p2;
}

void __cdecl Test_MyApiPointerCopy(wchar_t* p_in, wchar_t** p_out)
{
	if (p_in != nullptr)
	{
		*p_out = p_in;
	}
	else
	{
		p_out = nullptr;
	}
}
```

### Assert
「Assert::AreEqual()」などのモジュールを使って、テスト対象の関数の戻り値などが正しい値かどうかを確認する。下記のようなものがある。

|モジュール名|役目|
|---|---|
|AreEqual|オブジェクトが一致することを確認する|
|AreNotEqual|オブジェクトが一致しないことを確認する|
|IsNull|オブジェクトがNULLであることを確認する|
|IsNotNull|オブジェクトがNULLでないことを確認する|
など。ほかにも多数ある。

詳しくは、[MSのページ](https://docs.microsoft.com/ja-jp/visualstudio/test/microsoft-visualstudio-testtools-cppunittestframework-api-reference?view=vs-2019#general_asserts)を参照。

### Attribute
テスト用メソッドに対して、Attributeを設定できるらしいが、どうやって使うかはっきりわからず。詳しくは、[MSのページ](https://docs.microsoft.com/ja-jp/visualstudio/test/microsoft-visualstudio-testtools-cppunittestframework-api-reference?view=vs-2019#general_asserts)を参照。

# VisualStudio上でUnitTestを実施
[テスト] > [実行] の中のメニューを選んで、UnitTestを実施する。
または、[テスト] > [ウインドウ] > [テストエクスプローラ] でテストエクスプローラを開き、そこでもテストの実行ができる。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/352601/2888ab78-b179-d397-c8b2-eb66d232b7fe.png)

[実行]ではなく、[デバッグ]をすると、UnitTestのコード上でBreakを貼ったり、step実行ができる。

# コンソールでUnitTestを実施

### 必要物をダウンロード

テストエージェント`vstest.console.exe`が必要。下記の「VisualStudio2019のツール」からダウンロードする。

https://visualstudio.microsoft.com/ja/downloads/?q=agents
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/b44b902f-018c-32f5-3c81-685344ee619a.png)

### コンソールで実施
インストール後、「VSTest.Console.exe 」を使用して、テストを行う。
書式は、

vstest.console.exe "UnitTestのプロジェクトが吐いたDLL" "オプション"

``` UnitTest用.bat
rem Unitテスト実施用bat
rem x86用 ログを出力
rem ログは、「カレントディレクトリ\TestResults」に出力される
cd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow\"
vstest.console.exe "C:\Users\masa\Documents\myTortoiseGit\WPF-\020_CppDllProject\Debug\UnitTestCpp.dll" /platform:x86 /inIsolation /Logger:trx
pause
```

**※注意※**
上のbatでは、vstest.console.exeのあるフォルダとして`C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow\`としているが、VisualStudioを入れずに上のダウンロードからVSTESTのエージェントだけを入れた場合は、フォルダが別になる。その場合は、パスを自分のPCにあったものにすること。
例）私のPCの場合は下記にあった。
`C:\Program Files (x86)\Microsoft Visual Studio\2019\TestAgent\Common7\IDE\Extensions\TestPlatform\vstest.console.exe`


実行すると、このような窓が表示される。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/352601/aab2546d-0bb2-3197-71fb-3fdfc576e18f.png)



ログは、カレントディレクトリにできた「TestResults」フォルダ内に保存される。
.trxファイルを開くと、VisualStudioで結果を見ることができる。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/352601/889ac25f-a37c-c06d-8887-99dbaceac225.png)
※BEGIN_TEST_METHOD_ATTRIBUTEで設定したTEST_PRIORITYの値は、この結果で見れるっぽい。vstestの実行パラメータに、優先順位を指定できるので、そこに生きると思われる。(未検証)

# C＃のUnitTestについて

C++とほとんど同じやり方でUnitTestできる。下記のページを参照。

#### MsTestによるユニットテストの解説
https://qiita.com/mima_ita/items/55394bcc851eb8b6dc24
VSの設定からプロジェクトの設定、コードの書き方まで詳しく書いてくれている。
これ見れば一通りできそう。(コマンドラインでの実施も書いてくれている)

#### MS docs
公式ページ。単体テストのやり方はいくつかあるらしく(MSTestとか、xUnitとか)、その説明が書かれたページ。

https://docs.microsoft.com/ja-jp/visualstudio/test/walkthrough-creating-and-running-unit-tests-for-managed-code?view=vs-2017

MSTestのコマンドラインパラメータの一覧

https://docs.microsoft.com/ja-jp/visualstudio/msbuild/msbuild-command-line-reference?view=vs-2019

# 参照（C++）
Visual StudioでC ++用のMicrosoft単体テストフレームワークを使用する

https://docs.microsoft.com/ja-jp/visualstudio/test/how-to-use-microsoft-test-framework-for-cpp?view=vs-2019

Microsoft.VisualStudio.TestTools.CppUnitTestFramework APIリファレンス(Assertとか、Attributeとか)
https://docs.microsoft.com/ja-jp/visualstudio/test/microsoft-visualstudio-testtools-cppunittestframework-api-reference?view=vs-2019#cppUnitTestAssert_h