Cpputestの導入

# 背景
Pythonでの単体テストのやり方が書いてある[テスト駆動Python](https://www.shoeisha.co.jp/book/detail/9784798157603)を読んで、実際の業務で実践していました。
その効果を話していたところ、組み込み開発チームでも導入していきたいということだったので、組み込み開発でテスト駆動開発を行う[テスト駆動開発による組み込みプログラミング](https://www.amazon.co.jp/%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA%E3%81%AB%E3%82%88%E3%82%8B%E7%B5%84%E3%81%BF%E8%BE%BC%E3%81%BF%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E2%80%95C%E8%A8%80%E8%AA%9E%E3%81%A8%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%81%A7%E5%AD%A6%E3%81%B6%E3%82%A2%E3%82%B8%E3%83%A3%E3%82%A4%E3%83%AB%E3%81%AA%E8%A8%AD%E8%A8%88-James-W-Grenning/dp/4873116147)を参考にしながら、Cpputestを導入しました。
その際の導入手順をここにまとめます。

# 目的
+ 実機がなくても開発したコードの動作が検証可能な環境を構築する
    + ホスト(PC)での単体テスト実行環境を構築
    + ターゲット(組み込みLinux基板)での単体テスト実行は未実施

# 環境
+ OS: Debian GNU/Linux 9 on WSL
+ テストフレームワーク: Cpputest 3.8

# インストール手順
1. Cpputestをダウンロード
    + 後にCPPUTEST_HOMEという環境変数にCpputestをビルドしたディレクトリにパスを通すので、自分で管理しやすいパスにCpputestをgit cloneします。今回はhomeディレクトリにダウンロードします

    ```bash
    $ cd ~
    $ git clone https://github.com/cpputest/cpputest.git
    ```

1. Cpputestをビルド

    ```bash
    $ cd cpputest
    $ autoreconf . -i
    $ ./configure
    $ make tdd
    ```
1. 環境変数CPPUTEST_HOMEを定義する

    ```bash
    $ export CPPUTEST_HOME=~/cpputest
    ```

1. Cpputestが提供するユーティリティスクリプトをインストール

    ```bash
    $ export PATH=$PATH:${CPPUTEST_HOME}/scripts
    $ chmod a+x ${CPPUTEST_HOME}/scripts/NewProject.sh
    ```

# 実例
実際に新規プロジェクトを作成し、単体テストを作成する例を以下に示します。
簡単のために、足し算を行うadd関数の本体とそのテストを具体例として取り扱います。

1. Cpputestが提供するプロジェクト作成スクリプト(NewProject.sh)を使ってプロジェクトを作成する

    ```bash
    $ cd ~
    $ NewProject.sh add
    Copy template project
    include  Project.cproject  ProjectMakefile  Project.project src tests
    Change Name ./ProjectMakefile to addMakefile
    Change Name ./Project.project to add.project
    Change Name src/util/ProjectBuildTime.cpp to addBuildTime.cpp
    Change Name include/util/ProjectBuildTime.h to addBuildTime.h
    Change Name tests/util/ProjectBuildTimeTest.cpp to addBuildTimeTest.cpp
    You might want to modify the path for CPPUTEST_HOME in the Makefile.
    ```
    実行後、フォルダ構成は以下のようになっています。

    ```bash
    add
    ├── include
    │   └── util
    │       └── addBuildTime.h
    ├── Makefile
    ├── src
    │   └── util
    │       └── addBuildTime.cpp
    └── tests
        ├── AllTests.cpp
        └── util
            └── addBuildTimeTest.cpp
    ```    

1. テストが実行されるかを確認する

    ```bash
    $ cd add
    $ make
    compiling AllTests.cpp
    compiling addBuildTimeTest.cpp
    compiling addBuildTime.cpp
    Building archive lib/libadd.a
    a - objs/src/util/addBuildTime.o
    Linking add_tests
    Running add_tests
    .
    OK (1 tests, 1 ran, 1 checks, 0 ignored, 0 filtered out, 0 ms)
    ```

1. add関数の単体テストtest_add.cppを作成する

    ```bash
    $ touch tests/test_add.cpp
    ```

    ```cpp:test_add.cpp
    #include "CppUTest/TestHarness.h"

    extern "C"
    {
    #include "add.h"
    }

    TEST_GROUP(add)
    {
        void setup(){};

        void teardown(){};
    };

    TEST(add, InputPlusValue)
    {
        int ret = add(3, 4);
        CHECK_EQUAL(ret, 7);
    }

    ```

1. 作成した単体テストが実行されているか確認する

    ```bash
    $ make
    compiling test_add.cpp
    tests/test_add.cpp:5:17: fatal error: add.h: No such file or directory
    #include "add.h"
                    ^
    compilation terminated.
    /mnt/j/workspace/tdd_emb/cpputest/build/MakefileWorker.mk:506: recipe for target 'objs/tests/test_add.o' failed
    make: *** [objs/tests/test_add.o] Error 1
    ```

    当たり前といえば当たり前ですが、add.h, add.cに関して何も実装していないのでコンパイルエラーがでます。逆に言うと、コンパイルエラーが出ているのでCpputestはTEST(add, InputPlusValue)を認識していると言えます。

1. 単体テストが実行され失敗することを確認する
    まずは、コンパイルが通るところまでを確認します。
    includeディレクトリとsrcディレクトリにそれぞれadd.hとadd.cを作成し、add関数のインターフェイスと何を入力されても0を返す空実装を行います。
    
    ```bash
    $ touch include/add.h src/add.c
    ```
    ```C:add.h
    #ifndef _ADD_H_
    #define _ADD_H_

    int add(int, int);

    #endif /* _ADD_H_ */
    ```
    ```C:add.c
    int add(int a, int b)
    {
        return 0;
    }
    ```

   また、自動生成されたMakefileを変更します。
   INCLUDE_DIRSにある include/*をinclude/utilに変更します。
   そして、テストをビルドするとテストが失敗します。

   ```bash
    $ make
    compiling AllTests.cpp
    compiling test_add.cpp
    compiling addBuildTimeTest.cpp
    compiling add.c
    compiling addBuildTime.cpp
    Building archive lib/libadd.a
    a - objs/src/add.o
    a - objs/src/util/addBuildTime.o
    Linking add_tests
    Running add_tests
    .
    tests/test_add.cpp:18: error: Failure in TEST(add, InputPlusValue)
            expected <0>
            but was  <7>
            difference starts at position 0 at: <          7         >
                                                        ^

    .
    Errors (1 failures, 2 tests, 2 ran, 2 checks, 0 ignored, 0 filtered out, 3 ms)
   ```
   これで、最初に作成した単体テストが実行され期待した値と違う結果が返されたことが確認されました。この状態からadd関数の本実装を行います。

1. テストが通るようにadd関数を実装する
   ここは自分で修正してください。以下のような結果が得られるはずです。

    ```bash
    $ make
    compiling add.c
    Building archive lib/libadd.a
    r - objs/src/add.o
    r - objs/src/util/addBuildTime.o
    Linking add_tests
    Running add_tests
    ..
    OK (2 tests, 2 ran, 2 checks, 0 ignored, 0 filtered out, 1 ms)
    ```

# まとめ
Cpputestの導入方法とそれを使った簡単なサンプルをまとめました。
テスト駆動開発による組み込みプログラミングには、もっと複雑な例とテスト駆動を組み込み環境で行うにはどうしたらよいかの豊富な実例が示されておりますので、気になった方は是非お読みください。そして、教えてください。

# 参考資料
[cpputest-starter-project](https://github.com/jwgrenning/cpputest-starter-_project)