gmockについて勉強してみた(8):
グローバル関数にgmockを使ってみた

どもです。

前回までの投稿を通して、C++のクラス(のメンバ関数)のテストダブルを、gmockを使用して宣言/定義してきました。
一方で、C++で実装されたプログラム、アプリケーションでも、C言語形式の関数(グローバル関数)も実装されています。
そのため、gmockでグローバル関数の下位関数(グローバル関数)のテストダブルもgmockで宣言/定義できると、フレームワークの一貫性を保つことができ、テストが分かり易くなり、また保守性も向上します。
(グローバル関数のテストダブルは、独自のフレームワーク、実装になっていると、色々と問題の火種になります。)
使用するツール、フレームワークは全体として一貫していることが望ましいものです。

そこで今回のエントリでは、gmockを使用してグローバル関数のテストダブルを宣言/定義できるのか、できる場合には、どのように実装するのかを見てみます。

0. 作業環境:

作業環境です。
これまでと同様に、以下の環境で作業を行っています。

項目 内容
CPU Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz 3.20 GHz
RAM 16.0 GB (15.9 GB 使用可能)
OS Windows 10 Professional 22H2 (19045.5487)
IDE Visual Studio Community 2022 (64bit), Version 17.14.8
CMake version 4.1.0

1. サンプル関数

早速ですが、今回は以下のような関数を例に、検討を行います。

//サンプルコード
int Caller(int x) {
    return DoWhenCalled(x);  // テスト時に、DoWhenCalled()をテストダブルで置換する。
}

2. テストダブル

それでは、前出の関数の子関数である Called() をテストダブルの宣言・定義を考えます。

2.1. インタフェース

まず、インタフェースです。
ここでは、テストダブル化する子関数と同じI/Fを持つインタフェースを定義します。
具体的には、以下のコードです。

class ICalled {
public:
    virtual ~ICalled() = default;

    virtual int DoWhenCalled(int x) = 0;
};

2.2. 具象クラス

次に、このインタフェースの具象クラスです。
このクラスが、モックのクラス(本投稿では、テストダブルとの区別のために、「モッククラス」と呼称します。)となります。
ここでgmockのマクロを使用して、テストダブルを定義します。
これも、これまでの同行の中で紹介してきたコードと同じようなコードです。

class Called : public ICalled {
public:
    virtual ~Called() = default;

    MOCK_METHOD(int, DoWhenCalled, (int x), (override));
};

この辺は、定型文として覚えておくとよいですね。

2.3. テストダブル本体

今回テストダブル化したい関数である DoWhenCalled() の本体です。
これは、モックのクラスのオブジェクトのメソッドを呼び出すのみになります。
具体的には、以下のコードになります。

int DoWhenCalled(int x)
{
    return calledMock->DoWhenCalled(x);
}

2.4. モッククラスのインスタンス

モッククラスのインスタンスです。
これは、グローバル変数として宣言し、テストドライバにてインスタンスをセットします。
具体的には、以下のようなコードです。

// モッククラスへのポインタ
ICalled* calledMock;

// モッククラスのセット
Called mock;
calledMock = &mock;

2.5. テストダブルの動作

最後に、テストダブルの動作です。
テストダブルの動作は、これまでの投稿の中で紹介してきた内容と同じです。
テストダブル化の対象がグローバル関数となった場合でも、とくに違いはありません。

3. テストドライバ

次に、テストドライバです。
とは言っても、先述した通り、テストドライバは、これまで紹介してきた内容と同様です。

TEST(CalledTest, mockTest_001)
{
    int x = 1;

    Called mock;
    calledMock = &mock;

    EXPECT_CALL(mock, DoWhenCalled(1))
        .WillOnce(testing::Return(2));

    int ret_val = Caller(x);

    ASSERT_EQ(2, ret_val);
}

4. まとめ

今回は、gmockを使用してグローバル関数のテストダブルを宣言/定義できるのか、できる場合には、どのように実装するのかを見てみました。
その結果、gmockを使用したモッククラスを定義し、そのインスタンス、およびモックとなるメソッドをグローバル関数から呼び出すことで、テストダブル化できることを確認できました。
この際に使用するgmockのマクロ、メソッドに関しても、特別な違いはありませんでした。

違いがあるといえば、モッククラスのメソッドを参照するのが、テスト対象のクラス/メソッドではなく、テストダブル化の対象となるメソッドである、という点です。
この点は、DI(Dependency Injection/依存性注入)で注入する依存性がテストダブル化する子関数に変わった、と考えることができます。
そう考えれば、それほど難しいことではないと思います。

このように、メンバ関数(メソッド)であってもグローバル関数であっても、gmockを用いてテストダブル化することができます。

この内容が、誰かの助けになれば幸いです。
ではっ!