既存のC言語ファイルを変更せずにGoogleMockを使う

# 用語 [^1]
- TDD : テスト駆動開発（Test Driven Development）
- CUT : テスト対象コード（Code Under Test） 
- テストダブル（Test Double）
    - テスト中に、関数、データ、モジュール、ライブラリに成りすますもの。CUTは自分がテストダブルにたいしても本物のコラボレータと同じようにやりとりをする。

# 概要
[テスト駆動開発による組み込みプログラミング――C言語とオブジェクト指向で学ぶアジャイルな設計](https://www.oreilly.co.jp/books/9784873116143/)
で紹介されている、組み込みシステムによるテスト駆動開発の実践です。

TDDの構築として重要なのは、CUTに対する

- テストドライバー
- テストダブル（stub, spy, mock, fakeなどのこと）

をどう作るか？ですが、
実際の組み込みプロダクトでは、フルスクラッチでコードを書くことはないため

***<font color="red">- 既存コードが存在する
- C言語で書かれている
</font>***

の前提条件があります。
このため、プロダクトコードに対してTDD環境構築のために
 ***<font color="red">既存コードを変更したり、新規コードを追加したくない</font>*** 
ことがあります。

こちらを
- テストハーネス : GoogleTest
- テストダブル : GoogleMock
で構築していきます。

この記事では既存のCで書かれたプロダクトコードを変更せずにGoogleMockを使う方法の紹介になります。

# 手順
## フォルダ構成
```console:
ProjectRoot
├─gtest
│  ├─gtest_main.cpp
│  ├─led_control_unittest.cpp #"Mockを使ったテストケース"
│  ├─include
│  │  ├─gmock
│  │  └─gtest
│  ├─lib
│  └─mock_driver
│      ├─mock-LedDriver.cpp #"user_app/driver/LedDriver.cのMock"
│      └─mock-LedDriver.h
└─user_app #"本番環境で使用するプロダクトコード"
    ├─include
    │  ├─LedDriver.h #"LedDriver.c, mock-LedDriver.cpp 共通のヘッダ"
    │  └─led_control.h
    ├─driver
    │  ├─LedDriver.c     #"テスト対象コードが使うドライバ（この例では、このファイルがHWを制御すると仮定する。つまりMock化が必要"
    └─src
        ├─main.c
        └─led_control.c  #"CUT（テスト対象コード）"
```

## やりたいこと
テスト対象をled_controlとします。
この場合、LedDriverが依存コンポーネントのため、ここをMock化する必要があります。
これをプロダクトコードを変更せずに行いたいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540259/c95ed7bf-6f43-debf-7ff3-24e42c0cc67f.png)





## プロダクトコードとモックの切り替え
VisualStudio環境 = GoogleTest環境のため
MSVCの場合はmock_driver以下のコードをビルドするようにします。
`DRIVER_FILES`をgtest/CMakeLists.txtで使います。

```CMake:ProjectRoot/CMakeLists.txt
if(MSVC)
  message("Use Mock Driver")
  file(GLOB DRIVER_FILES ./gtest/mock_driver/*.cpp)
else()
  # 未実装だが本番環境のビルド時には本物のディレクトリを指定.
  message("Not Use Mock Driver")
  file(GLOB DRIVER_FILES ./user_app/driver/*.c)
endif(MSVC)
```

## Mockの実装
今回のテストで使う `LedDriver_Create`, `LedDriver_Destroy` を実装します。
ポイントはテストの定義ファイルから、Mockを渡せるように `SetMockLedDriver` を作ることです。

```c++:mock-LedDriver.h
#pragma once
#include "gtest/gtest.h"
#include "gmock/gmock.h"

class MockLedDriver {
public:
    MOCK_METHOD1(LedDriver_Create, void(uint16_t* adrs));
    MOCK_METHOD0(LedDriver_Destroy, void(void));
};
void SetdMockLedDriver(MockLedDriver* p);
```

cpp側です。
プロダクトコードから呼ばれた際にMockの関数につなぐようにします。

```c++:mock-LedDriver.cpp
#include "mock-LedDriver.h"
// -----------------------
// アプリケーションコードはCなのでextern "C" をしないと未解決のシンボルとなる.
// -----------------------
extern "C" {
#include "LedDriver.h"
}

static MockLedDriver* pMock;
void SetdMockLedDriver(MockLedDriver* p) {
    pMock = p;
}

// -----------------------
// ここにMockしたいAPIを書く
//   - アプリケーションコードからアクセスされたらMockのAPIに繋げる.
// -----------------------
void LedDriver_Create(uint16_t* address) {
    pMock->LedDriver_Create(0);
}

void LedDriver_Destroy(void) {
    pMock->LedDriver_Destroy();
}
```

## テストケースの実装
led_controlのInitializeLED, FinalizeLEDが1回ずつ呼ばれるようなテストケースを作っていきます。
`SetdMockLedDriver(&mock);`で、テストケースごとのモックの実態を設定します。

```c++:led_control_unitttest.cpp
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "mock_driver/mock-LedDriver.h"
extern "C"
{
#include "led_control.h"
}

class LedControlUnitTest : public testing::Test {
protected:
	void SetUp() override {
	}
	void TearDown() override {
	}
};

TEST_F(LedControlUnitTest, Initialize_Finalize_LED) {
	MockLedDriver mock;
	// 1回呼ばれることを期待（※呼ぶ前に記述する必要がある）
	EXPECT_CALL(mock, LedDriver_Create(testing::_))   // "_"は値は任意でOK.
		.Times(1);
	EXPECT_CALL(mock, LedDriver_Destroy())
		.Times(1);
	SetdMockLedDriver(&mock);
	InitializeLED();
	FinalizeLED();
}
```

## 結果
無事、passしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540259/6d19c001-7a4e-01dc-4319-73a0c70d8eab.png)

## コード
https://github.com/azukibar0713/cmake_freertos_visualstudio/tree/bcec5852cafbdb0cbfd93010dcb3cf9acebce24a

```console:
> cd build
> cmake ..
```

後に、"build/freertos_project.sln"を起動する。
※VisualStudio, CMakeをインストールしている必要があります。
　GoogleTest, Mockはレポジトリ内に含んでいます。

# 備考
ここまでやって気づきましたが
[テスト駆動開発による組み込みプログラミング～モック＆フラッシュドライバ編～](https://qiita.com/tomoyuki-nakabayashi/items/859f1ca077332d4197f6)
と、やっていることがよく似ています。初めから参考にさせてもらえばよかった。。

- グローバル変数は付かない
- 本番コードを変更せずに差し替えられる

が差分かな。

[^1]: [テスト駆動開発による組み込みプログラミング](https://www.oreilly.co.jp/books/9784873116143/)