C++のMocking Frameworkを試してみる

# 概要
Mocking Frameworkってすごいですよね。
C#でMoqを使ったとき、あまりの便利さに感動しました。
その一方で、リフレクションがある言語限定かなと思ってたのですが、C++にもあるんですね。
せっかくなので以下を試しに使ってみました。
- FakeIt
- HippoMocks
- Google Mock

動くコードは以下に置いてます。
https://github.com/minoru-nagasawa/CppMockFrameworkTrial/


# 比較
C言語みたいにグローバル関数が多い場合はHippoMocks一択
C++のクラスだけが対象なら、FakeItとHippoMocksで好みの方
個人的にはFakeItの書き方が好きなので、FakeItにグローバルな関数も対応してほしいところ。
Google Mockは無いな。
という感じです。

ただ、お試しに使ってみただけなので、ヘビーユーザの方のご意見聞きたいですね。

|                                 | FakeIt           | HippoMocks            | Google Mock          | コメント                                                                                                      |
|----------------------------------|------------------|-----------------------|----------------------|---------------------------------------------------------------------------------------------------------------|
| 導入しやすさ                     | 容易             | 容易                  | 面倒                 | Google Mock以外は1つのヘッダファイルをincludeするだけなので簡単                                                                                         |
| モッククラスの定義               | 不要             | 不要                  | 要                   | FakeItとHippoMocksは素晴らしい。<br />Google Mockは、モッククラスを作る必要があるのでいまいち |
| グローバル関数のモック     | 不可             | 可                    | 不可                 | HippoMocks有利                                                                                                |
| クラスのpublicメソッドのモック   | 可               | 可                    | 可                   | 基本機能                                                                                                      |
| クラスのprivateメソッドのモック  | 不可             | 不可                  | 不可                 | privateは変更できない                                                                                         |
| クラスのstaticメソッドのモック   | 不可             | 可                    | 不可                 | HippoMocks有利                                                                                                |
| 具象クラスの一部だけモック       | 可               | 不可                  | 可                   | FakeItとGoogle Mock有利                                                                                       |
| 価格                             | 無料             | 無料                  | 無料                 | 安心                                                                                                          |
| 最終更新日                       | 2018/11/10       | 2018/12/7             | 2015/8/26            | FakeItとHippoMocksは開発が継続してるように見える                                                              |
| googleでの検索数                 | 3220(hippomocks) | 4160(fakeit mock c++) | 38100("google mock") | 知名度は圧倒的にGoogle Mock                                                                                   |

# テスト対象
以下をテスト対象にしてます。

```cpp:ISample.h
class ISample
{
public:
    virtual int SampleMethod1(int lhs) = 0;
    virtual int SampleMethod2(int lhs, int rhs) = 0;
};
```

```cpp:SampleConcrete.h
class SampleConcrete : public ISample
{
public:
    SampleConcrete();
    ~SampleConcrete();
    int SampleMethod1(int lhs);
    int SampleMethod2(int lhs, int rhs);
    static int StaticMethod(int lhs);
};
```

```cpp:SampleConcrete.cpp
SampleConcrete::SampleConcrete()
{
}
SampleConcrete::~SampleConcrete()
{
}
int SampleConcrete::SampleMethod1(int lhs)
{
    return lhs;
}
int SampleConcrete::SampleMethod2(int lhs, int rhs)
{
    return lhs + rhs;
}
int SampleConcrete::StaticMethod(int lhs)
{
    return lhs;
}
```

```cpp:GlobalFunctions.cpp
int SampleMethodGlobal(int lhs)
{
    return lhs;
}
```

# FakeIt

## 開発サイト
https://github.com/eranpeer/FakeIt

クイックスタートはここ
https://github.com/eranpeer/FakeIt/wiki/Quickstart

## 導入方法

### 1. ヘッダファイルをコピー
自分のテストフレームワークにあったファイルを下記から持ってきてください。

- https://github.com/eranpeer/FakeIt/blob/master/single_header/gtest/fakeit.hpp
- https://github.com/eranpeer/FakeIt/blob/master/single_header/mstest/fakeit.hpp
- https://github.com/eranpeer/FakeIt/blob/master/single_header/boost/fakeit.hpp
- https://github.com/eranpeer/FakeIt/blob/master/single_header/catch/fakeit.hpp (Tested with Catch 2.0.1)
- https://github.com/eranpeer/FakeIt/blob/master/single_header/tpunit/fakeit.hpp
- https://github.com/eranpeer/FakeIt/blob/master/single_header/mettle/fakeit.hpp
- https://github.com/eranpeer/FakeIt/blob/master/single_header/qtest/fakeit.hpp
- https://github.com/eranpeer/FakeIt/blob/master/single_header/nunit/fakeit.hpp<br />(See caveats in config/nunit/fakeit_instance.hpp)
- https://github.com/eranpeer/FakeIt/blob/master/single_header/standalone/fakeit.hpp

### 2. インクルード＋α
以下のようにヘッダファイルをインクルードして、using namespace fakeitとしてください。
１つのヘッダファイルをインクルードするだけなので、簡単。

```cpp
#include "fakeit.hpp"
using namespace fakeit;
```

## 使い方
実際の使い方はこんな感じです。

```cpp:unittestFakeIt.cpp
            // 実装したいインターフェースを指定する
            Mock<ISample> mock;

            // 以下のようにすると、SampleMethod1を実行すると、必ず1が返ってくる
            When(Method(mock, SampleMethod1)).AlwaysReturn(1);

            // ISample型のオブジェクトを取得
            ISample& sample = mock.get();

            // ここまでの準備により、sample.SampleMethod1(10)が1を返すようになっている。
            Assert::AreEqual(1, sample.SampleMethod1(10));
```

```cpp:unittestFakeIt.cpp
            // 実装したいインターフェースを指定する
            Mock<ISample> mock;

            // 以下のようにUsingを使うと、メソッドの引数を指定できる。
            // SampleMethod1(1)を実行すると、必ず1が返ってくる
            // SampleMethod1(2)を実行しても、1は返ってこない
            When(Method(mock, SampleMethod1).Using(1)).AlwaysReturn(1);

            // ISample型のオブジェクトを取得
            ISample& sample = mock.get();

            // ここまでの準備により、sample.SampleMethod1(1)が1を返すようになっている。
            Assert::AreEqual(1, sample.SampleMethod1(1));

            // 引数を指定してないsample.SampleMethod1(2)を実行するとテスト失敗
            //sample.SampleMethod1(2);
```

```cpp:unittestFakeIt.cpp
            // 実装したクラスの場合は、オブジェクトを生成してMockに渡す
            SampleConcrete       obj;
            Mock<SampleConcrete> mock(obj);

            // 実装しているメソッドを一部だけを上書きできる
            When(Method(mock, SampleMethod1)).AlwaysReturn(5);

            // SampleConcrete型のオブジェクトを取得
            SampleConcrete& sample = mock.get();

            // 上書きしたメソッド
            Assert::AreEqual(5, sample.SampleMethod1(1));

            // 実装されている元のメソッド
            Assert::AreEqual(3, sample.SampleMethod2(1, 2));
```

# HippoMocks

## 開発サイト
http://www.hippomocks.com/Main_Page
https://github.com/dascandy/hippomocks

## 導入方法

### 1. ヘッダファイルをコピー
以下のヘッダファイルを持ってきてください。
https://github.com/dascandy/hippomocks/blob/master/HippoMocks/hippomocks.h

### 2. インクルード
以下のようにヘッダファイルをインクルードしてしてください。
１つのヘッダファイルをインクルードするだけなので、簡単。

```cpp
#include "hippomocks.h"
```

## 使い方
実際の使い方はこんな感じです。

```cpp:unittestHippoMocks.cpp
            // とりあえずMockRepositoryを生成
            MockRepository mocks;

            // ISample型のオブジェクトへのポインタを取得
            // mocksのデストラクタで解放されるので、sampleを直接解放する必要はない
            ISample* sample = mocks.Mock<ISample>();

            // 以下のようにすると、SampleMethod1を実行すると、必ず1が返ってくる
            mocks.ExpectCall(sample, ISample::SampleMethod1).Return(1);

            // ここまでの準備により、sample.SampleMethod1(10)が1を返すようになっている。
            Assert::AreEqual(1, sample->SampleMethod1(10));
```

```cpp:unittestHippoMocks.cpp
            // とりあえずMockRepositoryを生成
            MockRepository mocks;

            // ISample型のオブジェクトへのポインタを取得
            // mocksのデストラクタで解放されるので、sampleを直接解放する必要はない
            ISample* sample = mocks.Mock<ISample>();

            // 以下のようにWithを使うと、メソッドの引数を指定できる。
            // SampleMethod1(1)を実行すると、必ず1が返ってくる
            // SampleMethod1(2)を実行しても、1は返ってこない
            mocks.ExpectCall(sample, ISample::SampleMethod1).With(1).Return(1);

            // ここまでの準備により、sample.SampleMethod1(1)が1を返すようになっている。
            Assert::AreEqual(1, sample->SampleMethod1(1));

            // 引数を指定してないsample.SampleMethod1(2)を実行するとテスト失敗
            //sample->SampleMethod1(2);
```

```cpp:unittestHippoMocks.cpp
            // とりあえずMockRepositoryを生成
            MockRepository mocks;

            // SampleConcrete型のオブジェクトへのポインタを取得
            SampleConcrete* sample = mocks.Mock<SampleConcrete>();

            // 実装しているメソッドを上書きできる
            mocks.ExpectCall(sample, ISample::SampleMethod1).Return(5);

            // 上書きしたメソッド
            Assert::AreEqual(5, sample->SampleMethod1(1));

            // 実装されている元のメソッドは呼べない。
            // NotImplementedExceptionが吐かれる。
            // https://stackoverflow.com/questions/41547660/hippomocks-throws-notimplementedexception-when-not-specifying-expectation
            //sample->SampleMethod2(1, 2);
```

```cpp:unittestHippoMocks.cpp
            // グローバル関数を上書きする前は、実装した通りのふるまい
            Assert::AreEqual(1, SampleMethodGlobal(1));

            // とりあえずMockRepositoryを生成
            MockRepository mocks;

            // グローバル関数も上書きできる
            mocks.ExpectCallFunc(SampleMethodGlobal).Return(10);

            // 上書きしたメソッド
            Assert::AreEqual(10, SampleMethodGlobal(1));
```

```cpp:unittestHippoMocks.cpp
            // クラスのstaticメソッドを上書きする前は、実装した通りのふるまい
            Assert::AreEqual(1, SampleConcrete::StaticMethod(1));

            // とりあえずMockRepositoryを生成
            MockRepository mocks;

            // クラスのstaticメソッドも上書きできる
            mocks.ExpectCallFunc(SampleConcrete::StaticMethod).Return(10);

            // 上書きしたメソッド
            Assert::AreEqual(10, SampleConcrete::StaticMethod(1));
```


# Google Mock

## 開発サイト
https://github.com/google/googlemock/tree/master/googlemock

チートシートがここ
https://github.com/google/googlemock/blob/master/googlemock/docs/CheatSheet.md

## 導入方法

## 1. Nugetからインストール
本家の導入方法には、自分でビルドする必要があって面倒です。
Visual Studioで開発している場合はNugetで取得できましたので、こっちを使います。
以下のようにGoogleMockで検索して、「googletestmock.v141」をインストールしてください。
GoogleオフィシャルのNugetパッケージはうまく動かなかったので、こっちの方がいいです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178867/a4829e90-70e3-59c9-7ebb-ebe5374740ca.png)

### 2. インクルード
以下のようにヘッダファイルをインクルードしてしてください。

```cpp
#include "gmock/gmock.h"
```

### 3. モッククラスを作成
Google Mockでは、モッククラスを自分で作成する必要があります。
以下のように作成します。
MOCK_METHOD#が定義されてますので、引数の数に合わせて使用します。

```cpp
    // インターフェースのモッククラス
    class MockSample : public ISample
    {
    public:
        MOCK_METHOD1(SampleMethod1, int(int lhs));
        MOCK_METHOD2(SampleMethod2, int(int lhs, int rhs));
    };
    // 具象クラスのモッククラス
    class MockSampleConcrete : public SampleConcrete
    {
    public:
        // 実装しているメソッドを一部だけを指定できる
        MOCK_METHOD1(SampleMethod1, int(int lhs));
    };
```

## 使い方
実際の使い方はこんな感じです。

```cpp:unittestGoogleMock.cpp
            // 事前に準備したMock用のオブジェクトを生成
            MockSample mock;

            // 以下のようにすると、SampleMethod1を実行すると、必ず1が返ってくる
            EXPECT_CALL(mock, SampleMethod1(testing::_)).WillRepeatedly(testing::Return(1));

            // ここまでの準備により、sample.SampleMethod1(10)が1を返すようになっている。
            Assert::AreEqual(1, mock.SampleMethod1(10));
```

```cpp:unittestGoogleMock.cpp
            // 事前に準備したMock用のオブジェクトを生成
            MockSample mock;

            // 以下のように、メソッドの引数を指定できる。
            // SampleMethod1(1)を実行すると、必ず1が返ってくる
            // SampleMethod1(2)を実行しても、1は返ってこない
            EXPECT_CALL(mock, SampleMethod1(1)).WillRepeatedly(testing::Return(1));

            // ここまでの準備により、sample.SampleMethod1(1)が1を返すようになっている。
            Assert::AreEqual(1, mock.SampleMethod1(1));

            // mock.SampleMethod1(2)を実行すると、指定しないので0が返ってくる
            Assert::AreEqual(0, mock.SampleMethod1(2));
```

```cpp:unittestGoogleMock.cpp
            // 事前に準備したMock用のオブジェクトを生成
            MockSampleConcrete mock;

            // 実装しているメソッドを一部だけを上書きできる
            EXPECT_CALL(mock, SampleMethod1(1)).WillRepeatedly(testing::Return(1));

            // 上書きしたメソッド
            Assert::AreEqual(1, mock.SampleMethod1(1));

            // 実装されている元のメソッド
            Assert::AreEqual(3, mock.SampleMethod2(1, 2));
```

# 最後に
今回はC++のMocking Frameworkを比較してみました。
お試しレベルで使ってみた感じでは非常に便利でした。
ただ、実際の大きなプロジェクトで使用すると、見えてない課題が出て来るかもしれませんね。

それにしても、FakeItとHippoMocksはどういう仕組みで実現してるんだろう。
コード見てもtemplateが駆使してあって全然わからない。。。