LoginSignup
11
12

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-02-01

概要

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は素晴らしい。
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

テスト対象

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

ISample.h
class ISample
{
public:
    virtual int SampleMethod1(int lhs) = 0;
    virtual int SampleMethod2(int lhs, int rhs) = 0;
};
SampleConcrete.h
class SampleConcrete : public ISample
{
public:
    SampleConcrete();
    ~SampleConcrete();
    int SampleMethod1(int lhs);
    int SampleMethod2(int lhs, int rhs);
    static int StaticMethod(int lhs);
};
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;
}
GlobalFunctions.cpp
int SampleMethodGlobal(int lhs)
{
    return lhs;
}

FakeIt

開発サイト

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

導入方法

1. ヘッダファイルをコピー

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

2. インクルード+α

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

#include "fakeit.hpp"
using namespace fakeit;

使い方

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

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));
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);
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. インクルード

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

#include "hippomocks.h"

使い方

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

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

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

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

            // 上書きしたメソッド
            Assert::AreEqual(10, SampleMethodGlobal(1));
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/blob/master/googlemock/docs/CheatSheet.md

導入方法

1. Nugetからインストール

本家の導入方法には、自分でビルドする必要があって面倒です。
Visual Studioで開発している場合はNugetで取得できましたので、こっちを使います。
以下のようにGoogleMockで検索して、「googletestmock.v141」をインストールしてください。
GoogleオフィシャルのNugetパッケージはうまく動かなかったので、こっちの方がいいです。
image.png

2. インクルード

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

#include "gmock/gmock.h"

3. モッククラスを作成

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

    // インターフェースのモッククラス
    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));
    };

使い方

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

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));
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));
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が駆使してあって全然わからない。。。

11
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
12