1. 程式人生 > >Microsoft Unit Testing Framework for C++

Microsoft Unit Testing Framework for C++

Author: kagula
Date: 2018-05-11

Introduction

       VC自帶多個VC++單元測試工具,其中Microsoft Unit Testing Framework原生支援Test Explorer,  這裡通過三個例子來學習Microsoft Unit Testing Framework.


Environment

 [1]Visual Studio 2017, update 7.1


Content  

一、最簡單的測試
這種情況只發生在“測試驅動”編碼方式中,現實世界我很少用到,這裡只是為了大致有個瞭解。
Step1:新建我們的第一個測試單元UnitTest1,新建solution
Visual C++ -> Test -> Native Unit Test Project

Step2:
用下面的程式碼替代建立的預設模板

unittest1.cpp

#include "stdafx.h"
#include "CppUnitTest.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

#include <iostream>

using namespace std;

TEST_MODULE_INITIALIZE(ModuleInitialize)
{
	Logger::WriteMessage("In Module Initialize");
}

TEST_MODULE_CLEANUP(ModuleCleanup)
{
	Logger::WriteMessage("In Module Cleanup");
}

/*
第一次執行本unit test, 使用Ctrl+R+A 執行全部測試.
以後你可以在Test Explorer視窗中執行指定的測試函式.
*/
namespace KagulaUnitTest
{		
	//模擬我的第一個待測試的function集合
	TEST_CLASS(myFirstFunctionSetForTest)
	{
	public:
		
		TEST_METHOD(TestMethod1)
		{
			//執行unit test不會開啟控制檯視窗, 所以你也不會看到下面這條程式碼的任何std輸出.
			cout << "hello,World!" << endl;
		}

		TEST_METHOD(TestMethod2)
		{
			//模擬耗時的操作.
			for (size_t i = 0; i < 1000; i++)
			{
				if ((i % 100) == 0)
				{
					//VisualStudio2017Update7.1有個很嚴重的缺陷,有時候,你改了程式碼,但是使用Test Explorer中的"Run selected Tests"選單項,
					//執行的還是老的程式碼, 這時候, 你得rebuild solution後再執行.
					//所以修改程式碼後,  修改下面的字串輸出, 從output視窗中檢視是不是修改已經生效很重要.
					Logger::WriteMessage("d.");
				}
			}//for
		}//test method
	};//test class

	//模擬我的第二個待測試的function集合
	TEST_CLASS(mySecondFunctionSetForTest)
	{
	public:

		//Assert的具體用法參考下面的地址
		//https://docs.microsoft.com/en-us/visualstudio/test/microsoft-visualstudio-testtools-cppunittestframework-api-reference#general_asserts
		TEST_METHOD(TestMethod2_1)
		{
			Logger::WriteMessage("TestMethod2_1");
			int expected = 0;
			int actual = 0;
			Assert::AreEqual(expected, actual);
			Assert::AreEqual(expected, actual, L"are not equal assert!");
		}

		TEST_METHOD(TestMethod2_2)
		{
			Logger::WriteMessage("TestMethod2_2");
			Assert::Fail(L"Fail");
		}
	};
}

Step3:
使用選單項[test]->[Run]->[All Tests]或則直接使用快捷鍵Ctrl+R+A
而不是習慣的F5或Ctrl+F5,之後會出現Test Explorer子視窗,如下圖。


Output子視窗,Show output from下拉框選項改為Tests,然後你就可以看到單元測試專案的輸出。


二、對已存exe的測試這才是我最常見的使用場合

Step1:

建個簡單的console程式c++專案,用來模擬已經存在的EXE程式專案

我寫了個很簡單的程式碼Source.cpp

#include <iostream>
using namespace std;

//這個函式用來演示你寫的函式如何被"單元測試"工具呼叫
int myFunction()
{
	return 1;
}

int main(int argc, char* argv[])
{
	cout << "hello, World!" << endl;



	return 0;
}

Step2:為專案新增Unit Test依賴的標頭檔案和庫檔案搜尋路徑.
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\VS\UnitTest\include
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\VS\UnitTest\lib

Step3:
使用下面的步驟新增unit test檔案
Solution Explorer -> Add -> New Item -> C++ Unit Test.

如果沒有“C++ Unit Test”模板,就添加個普通的cpp檔案

Test.cpp

//若不需要單元測試,把UNIT_TEST巨集註釋掉就可以了!
//否則exe程式會去找我們不需要的unit test依賴的dll.
#define UNIT_TEST

#ifdef UNIT_TEST
#include <CppUnitTest.h>

//假設這是我待做unit test測試的函式.begin
extern int myFunction();
//假設這是我待做unit test測試的函式.end


using namespace Microsoft::VisualStudio::CppUnitTestFramework;

BEGIN_TEST_MODULE_ATTRIBUTE()
TEST_MODULE_ATTRIBUTE(L"Date", L"2010/6/12")
END_TEST_MODULE_ATTRIBUTE()

TEST_MODULE_INITIALIZE(ModuleInitialize)
{
	Logger::WriteMessage("In Module Initialize");
}

TEST_MODULE_CLEANUP(ModuleCleanup)
{
	Logger::WriteMessage("In Module Cleanup");
}

/*
第一次執行本unit test, 使用Ctrl+R+A 執行全部測試.
以後你可以在Test Explorer視窗中執行指定的測試函式.
*/
namespace KagulaUnitTest
{
	TEST_CLASS(myFunctionSetForTest)
	{
	public:

		//Assert的具體用法參考下面的地址
		//https://docs.microsoft.com/en-us/visualstudio/test/microsoft-visualstudio-testtools-cppunittestframework-api-reference#general_asserts
		TEST_METHOD(TestMethod2_1)
		{
			Logger::WriteMessage("TestMethod1");
			int expected = 0;
			int actual = myFunction();
			Assert::AreEqual(expected, actual, L"are not equal assert!");
		}
	};
}
#endif
Step4:
把專案的Configuration Type屬性從原來的Application (.exe)改為Dynamic Library (.dll)
否則你單元測試程式碼是不能正常執行的。

Step5:
使用Ctrl+R+A執行所有的單元測試,或使用主選單項
Test->Run->All->Tests選單命令執行所有單元測試。
接下來你可以探索Test Explorer窗口裡的unit test功能的。

Final Step:
[1]
如果你專案unit test沒問題了,再把project的Configuration Type屬性 改回Application (.exe)
[2]
如果改回EXE後,Ctrl+F5執行程式,console一閃而過,使用下面的設定
Configuration Properties -> Linker -> System -> SubSystem -> Console(/SUBSYSTEM:CONSOLE)

三、對已存dll專案的測試
Step1:新建個空白專案,當作是我們待測的現存dll專案。

Installed -> Visual C++ -> General -> Empty Project
新起專案名稱為UnitTest3
Configuration Type 設為 Dynamic Library(.dll)
這樣編譯原始碼後會生成dll lib等兩個檔案.

我這個專案,由兩個原檔案組成,原始碼內容如下

CDemoLib.h

#ifndef _LIB_H_
#define _LIB_H_

#ifdef _WIN32

#ifdef _WINDLL //VC2017新建專案後,改生成方式為dll, 會自帶這個巨集定義.
#define LIB_API  extern "C" __declspec(dllexport)
#else
#define LIB_API  extern "C" __declspec(dllimport)
#endif

//我不喜歡編寫非C語言風格的介面供呼叫者使用, 除非時間緊.
#ifdef _WINDLL
#define CLASS_EXPORT __declspec(dllexport)
#else
#define CLASS_EXPORT __declspec(dllimport)

#endif

#else
//非Windows環境不需要那麼麻煩直接定一個空白的就可以了.
#define  LIB_API 
#endif


LIB_API int add(int x, int y);

class CLASS_EXPORT MyDemoClass
{
public:
	int add(int a, int b);
};

#endif

CDemoLib.cpp

#include "CDemoLib.h"

int add(int x, int y)
{
	return x + y;
}

int MyDemoClass::add(int a, int b)
{
	return a + b;
}

Step 2:
在當前solution下新建單元測試專案
Visual C++ -> Test -> Native Unit Test Project
我把project起名為MyUnitTester。

這個專案只有一個原始檔,清單如下:

#include "stdafx.h"
#include "CppUnitTest.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

#include "../UnitTest3/CDemoLib.h"

//新增All Configuration 庫檔案搜尋路徑: $(SolutionDir)$(Configuration);
#pragma comment(lib, "UnitTest3.lib")

namespace MyUnitTester
{		
	TEST_CLASS(UnitTest1)
	{
	public:
		
		TEST_METHOD(TestMethod1)
		{
			Logger::WriteMessage("MyUnitTester....");
			int expect = 0;
			int actual = add(0, 0);
			//C風格介面  測試
			Assert::AreEqual(expect, actual, L"are not equal assert!");

			MyDemoClass mdc;
			actual = mdc.add(0, 0);
			//C++風格介面  測試
			Assert::AreEqual(expect, actual, L"are not equal assert!");
		}
	};
}

Final Step:

然後你就可以Ctrl+R+A執行單元測試了。


Remark:

Q: 為什麼不用boost.test?
A 因為在Visual Studio 中 Debug方式執行,console會一閃而過.


Reference

[1]<<Use the Microsoft Unit Testing Framework for C++ in Visual Studio>>
https://docs.microsoft.com/en-us/visualstudio/test/how-to-use-microsoft-test-framework-for-cpp
[2]<<Microsoft.VisualStudio.TestTools.CppUnitTestFramework API Reference>>
https://docs.microsoft.com/en-us/visualstudio/test/microsoft-visualstudio-testtools-cppunittestframework-api-reference#general_asserts