Google Testing/Mocking frameworkでCプログラム内のWindowsライブラリをモック


はじめに

どうも!生産技術部のエンジニアです。今回はGoogle C++ Testing/Mocking frameworkを利用して、C言語で記述したプログラム内のWindowsライブラリをモックする方法を紹介します。作成したプログラムはlsg1050-controllertestフォルダにあります。

環境

  • 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であるPurgeCommWriteFileを利用します。

src/controller.c
/************************/
/*!
 * @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/WriteFilePurgeCommMock/WriteFileMockと定義し、#include "../src/controller.c"とすることで、controller.cを書き換えることなくMock関数を利用する事ができます。

test/test.cc 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クラスは、公式の「モッククラスを書く」に従い作成します。

test/test.cc 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を利用できるようになります。

test/test.cc Mock関数・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を利用し、テスト結果を確認しました。正しくモック出来ているんではないでしょうか?

ご参考