はじめに
どうも!生産技術部のエンジニアです。今回はGoogle C++ Testing/Mocking frameworkを利用して、C言語で記述したプログラム内のWindowsライブラリをモックする方法を紹介します。作成したプログラムはlsg1050-controllerのtest
フォルダにあります。
環境
- Google Test/Mock framework: 1.8.1
- Microsoft(R) C/C++ Optimizing Compiler: 19.23.28105.4 for x64
前提条件
Google Test/Mock frameworkの導入が実施済みであること。
テスト(Google Testing/Mocking framework)を利用するメリット
私は生産技術部で検査装置の設計を行っています。検査装置は検査する対象の製品に対して、電圧・電流・波形・デジタル信号などを入力し、製品から出力された電圧・電流・波形・デジタル信号などを読み取ることで検査を行います。製品に対する入力および出力は、電圧電流発生器や計測器を制御することで行います。検査装置の開発段階では、これらの計測器が手元にない状態で、計測器の制御プログラムを作成する必要がある場合があります。こういったシチュエーションでも、柔軟に動作検証が出来るようにGoogle Testing/Mocking frameworkを利用します。また、ハードウェアが無くても動作可能である事は、新入社員のプログラム教育においても、ハードウェアを用意する必要がなく低コストで実施できる事に繋がります。
Windowsライブラリをモック
controller.c
をテスト対象のプログラムとして、電子負荷装置(LSG 1050)を制御するプログラムを作成します。LSG 1050
はUSB CDCプロトコルで制御する仕様となっており、Windows環境に標準で入っている仮想COMポートを生成するドライバ(usbser.sys)を用いることで制御する事ができます。
テスト対象のプログラム
write_line_lsg_1050を作成します。この関数は、LSG 1050
に対して制御コマンドを送信します。制御コマンドを送信するために、Windows用の制御APIであるPurgeCommとWriteFileを利用します。
/************************/
/*!
* @brief 電子負荷(LSG 1050)にコマンドを送信
* @param [in] hCom A handle to the communications resource.
* @param [in] format 出力コマンド
* @param [in] ... コマンドに設定する引数
* @return 0 正常終了<br>
* 1 PurgeComm異常終了<br>
* 2 WriteFile異常終了
* @ingroup write_line_group
*/
/************************/
int write_line_lsg_1050(HANDLE *hCom,char *format, ... ){
va_list va;
DWORD dwBytesWrite = 10;
BOOL fSuccess;
char message[256];
int messageLenght = 0;
// Accesses variable-argument lists.
va_start(va, format);
// Write formatted output using a pointer to a list of arguments.
vsprintf((char*)message, format, va);
va_end(va);
# ifdef DEBUG_LSG_1050
Print("message == %s",message);
# endif
messageLenght = strlen(message);
// EOF is "LF"
message[messageLenght] = 0xa;
// Clear send/recv buffer
fSuccess = PurgeComm(*hCom, PURGE_RXABORT | PURGE_RXCLEAR);
if(!fSuccess){
Print("PurgeComm failed with error %d.\n",GetLastError());
return (1);
}
// Send command to LSG1050
fSuccess = WriteFile(
*hCom,
message,
messageLenght +1,
&dwBytesWrite,
NULL
);
if(!fSuccess){
Print("WriteFile failed with error %d.\n",GetLastError());
return (2);
}
return (0);
}
テストプログラム
Google Test/Mock frameworkでPurgeComm/WriteFileをモックします。これらの関数は、Windows.h
ライブラリに存在し、C言語でこれらをモックするためには工夫がいります。Windows.h
内のPurgeComm/WriteFileを直接利用せず、PurgeCommMock/WriteFileMockというMock関数を作成し利用します。
Mock関数
Windows.h
をインクルードした後、PurgeComm/WriteFileをPurgeCommMock/WriteFileMockと定義し、#include "../src/controller.c"
とすることで、controller.c
を書き換えることなくMock関数を利用する事ができます。
# include <windows.h>
# include <tchar.h>
# include <stdio.h>
# define PurgeComm PurgeCommMock
BOOL PurgeCommMock(
HANDLE hFile,
DWORD dwFlags
);
# define WriteFile WriteFileMock
BOOL WriteFileMock(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);
# include "../src/controller.c"
Mockクラス
Mockクラスは、公式の「モッククラスを書く」に従い作成します。
class MockWin{
public:
MockWin() {}
MOCK_METHOD2(PurgeCommMockA, BOOL(
HANDLE hFile,
DWORD dwFlags
)
);
MOCK_METHOD5(WriteFileMockA, BOOL(
HANDLE hFile,
LPCSTR lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
)
);
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(MockWin);
};
Mock関数・Mockクラスを接続
定義したMock関数のPurgeCommMock/WriteFileMockから、MockクラスのPurgeCommMockA/WriteFileMockAを呼び出す事で、Google Mockを利用できるようになります。
MockWin* winLib;
BOOL PurgeCommMock(
HANDLE hFile,
DWORD dwFlags
){
return winLib->PurgeCommMockA(hFile, dwFlags);
}
BOOL WriteFileMock(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
){
return winLib->WriteFileMockA(hFile, (LPCSTR)lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
}
最後に
Google Testing/Mocking frameworkを利用し、C言語で記述したプログラム内のWindowsライブラリをモックしました。GoogleTest Adapterを利用し、テスト結果を確認しました。正しくモック出来ているんではないでしょうか?