GoogleMockで非メンバ関数をモック化するヘッダ

# 概要
GoogleMockでは、非メンバ関数のモック化ができないが、[MockFunction](http://opencv.jp/googlemockdocs/cheatsheet.html#cheatsheet-mock-classes)([実装サンプル](http://opencv.jp/googlemockdocs/cookbook.html#cookbook-using-check-points))が提供されている。
これを使いやすくするための２つのヘッダを作成した。
注意：C++17以上

- モック定義用のマクロ
- モック利用のための構造体定義

以下の記事の内容を簡単に実現できるようにした。

https://qiita.com/poponta/items/6e8cd5f98846c098d32c

https://qiita.com/azuki_bar/items/4849d9266f446a1b36ec

# 制限事項

以下が未対応
- 可変長引数
- オーバーロードがある関数（解決ができない）

# 本体

## モック定義用のマクロ
モック関数を定義するヘッダ

```c++:mock_function_wrapper.h
#pragma once

#include <cassert>
#include <map>

#include <gmock/gmock-spec-builders.h>

template <auto func>
using mock_ptr_type = ::testing::MockFunction<std::remove_pointer_t<decltype(func)>>*;

inline std::map<void*, void*> g_funcptr_mock_map_for_test;

/**
 * @def DEFINE_MOCK_FUNC
 * @brief モック定義マクロ
 *
 * @code
 * // 以下の関数定義があるとき
 * int my_open(const char* name);
 * // 以下の記述でモックを定義する。
 * DEFINE_MOCK_FUNC(int, my_open , (const char* name), ( name ))
 * @endcode

 * @param ret_type  戻り値の型
 * @param func_name 関数名
 * @param arg_def   引数定義。関数の定義の（）をそのまま指定する
 * @param called_arg 呼び出されるときの引数
 *
 *
 */
#define DEFINE_MOCK_FUNC(ret_type, func_name, arg_def, called_arg) \
ret_type func_name arg_def {          \
	auto mock = reinterpret_cast< mock_ptr_type<func_name> >( g_funcptr_mock_map_for_test[reinterpret_cast<void*>(func_name)] ); \
	assert(mock != nullptr);          \
	return mock->Call called_arg;     \
}

```

## モック利用のための構造体定義
テストスイートを実装するソースがインクルードするヘッダ。
モックが使えるようになる。

```c++:MockBinder.h
#include <map>
#include <gmock/gmock-spec-builders.h>
#include <type_traits>

/**
 * @struct MockBinder
 * @brief MockFunctionの Wrapper。関数ポインタに対応するMockFunctionを生成し、マップに登録する。
 *
 * @tparam FuncPtr モック化する関数ポインタ。関数名を指定する。
 */
template<auto FuncPtr>
struct MockBinder
{
	using mock_type = ::testing::MockFunction<std::remove_pointer_t<decltype(FuncPtr)>>;
	mock_type func;

	// RAII
	MockBinder (){set_mock(&func);  }  // MockFunctionを登録。RAIIでポインタの登録、削除を行う
	~MockBinder(){set_mock(nullptr);}  // マップから削除する

private :
	/**
	 * @fn void set_mock(mock_type*)
	 * @brief モックマップにMockFunctionを登録する
	 *
	 * @param ptr 登録するMockFunctionのポインタ
	 */
	void set_mock( mock_type* ptr)
	{
		// 関数ポインタのアドレスをキーとしたマップ。MockFunctionのアドレスを値として登録する。
		extern std::map<void*, void*> g_funcptr_mock_map_for_test;
		g_funcptr_mock_map_for_test[reinterpret_cast<void*>(FuncPtr)] = ptr;
	}
};
```

# 利用サンプル

## テストスイート
```c++:
#include "proc.h" // proc()関数の宣言。今回省略。

#include <gtest/gtest.h>


#include "my_driver.h"     // モック化する関数の宣言
#include "MockBinder.h.h"  // モック利用のための構造体定義

// テストが失敗することを確認するためのテスト
TEST(test, test_faulse)
{
	MockBinder<my_open> mock;
	EXPECT_CALL(mock.func, Call(::testing::_)).WillOnce(::testing::Return(2));
	EXPECT_EQ(2, proc("path"));
}

// テストが成功することを確認するためのテスト
TEST(test, test_success)
{
	MockBinder<my_open> mock;
	EXPECT_CALL(mock.func, Call(::testing::_)).WillOnce(::testing::Return(1));
	EXPECT_EQ(2, proc("path"));
}

```

## モック定義
```c++:
#include "mock_function_wrapper.h"

DEFINE_MOCK_FUNC(int, my_open , (const char* name)                          , ( name )            )
DEFINE_MOCK_FUNC(int, my_read , (int id, void* dest, std::size_t size)      , ( id,  dest,  size) )
DEFINE_MOCK_FUNC(int, my_write, (int id, const void* src, std::size_t size) , ( id,  src,  size)  )
DEFINE_MOCK_FUNC(int, my_stat , (int id)                                    , ( id)               )
DEFINE_MOCK_FUNC(int, my_close, (int id)                                    , ( id)               )
```

## 関数定義（モック化したい関数の宣言）
```c++:my_driver.h
#pragma once
#include <cstddef>

int my_open(const char* name);
int my_read(int id, void* dest, std::size_t size);
int my_write(int id, const void* dest, std::size_t size);
int my_stat(int id);
int my_close(int id);
```

## テスト対象のロジック
```c++:proc.cpp
#include "my_driver.h"

int proc(const char* path)
{
	int ret = my_open( path );
	ret += 1;
	return ret;
}
```


## おまけのMakefile

```:Makefile
# コンパイラとフラグ
CXX := g++
CXXFLAGS := -std=c++17 -Wall -Wextra #-ftime-report

# GTest / GMock フラグ
GTEST_CFLAGS := $(shell pkg-config --cflags gtest gmock)
GTEST_LIBS   := $(shell pkg-config --libs gtest gmock) -lgtest_main

# ソースとオブジェクトファイル
SRC_FILES := $(wildcard *.cpp)
OBJ_FILES := $(SRC_FILES:.cpp=.o)

# 実行バイナリ
TARGET := test_runner

.PHONY: all clean test

# デフォルト
all: $(TARGET)

# バイナリ生成
$(TARGET): $(OBJ_FILES)
	$(CXX) $(CXXFLAGS) $^ -o $@ $(GTEST_LIBS)

# .o ファイル生成ルール
%.o: %.cpp
	$(CXX) $(CXXFLAGS) $(GTEST_CFLAGS) -c $< -o $@

# テスト実行
test: $(TARGET)
	./$(TARGET)

# クリーン
clean:
	rm -f *.o $(TARGET)

```

## 実行結果
```
$ make test
g++ -std=c++17 -Wall -Wextra  -DGTEST_HAS_PTHREAD=1 -I/usr/local/include -c proc.cpp -o proc.o
g++ -std=c++17 -Wall -Wextra  -DGTEST_HAS_PTHREAD=1 -I/usr/local/include -c my_driver_mock.cpp -o my_driver_mock.o
g++ -std=c++17 -Wall -Wextra  -DGTEST_HAS_PTHREAD=1 -I/usr/local/include -c proc_test.cpp -o proc_test.o
g++ -std=c++17 -Wall -Wextra  proc.o my_driver_mock.o proc_test.o -o test_runner -L/usr/local/lib -lgmock -lpthread -lgtest -lpthread -lgtest_main
./test_runner
Running main() from xxx/gtest_main.cc
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from test
[ RUN      ] test.test_faulse
proc_test.cpp:21: Failure
Expected equality of these values:
  2
  proc("path")
    Which is: 3

[  FAILED  ] test.test_faulse (0 ms)
[ RUN      ] test.test_success
[       OK ] test.test_success (0 ms)
[----------] 2 tests from test (2 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (3 ms total)
[  PASSED  ] 1 test.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] test.test_faulse

 1 FAILED TEST
Makefile:31: recipe for target 'test' failed
make: *** [test] Error 1
```