VisualStudioCode+PlatformIOでユニットテストしてみました


VisualStudioCode+PlatformIOでユニットテストしてみました  

# 1 はじめに  
VisualStudioCode+PlatformIOでユニットテスト環境を構築して実行するまでの手順を忘れないために記事にします  

PlatformIOではunityというユニットテストフレームワークを利用します  
しかし、unityのテストグループのマクロがサポートされていませんでした  
テストを繰り返すときに不便なんでテストグループのマクロを追加しました  

unity https://github.com/ThrowTheSwitch/Unity

## 1.1 追加するマクロ

RUN_TEST_GROUP  

TEST_GROUP  
TEST_SETUP  
TEST_TEAR_DOWN  

# 2 プロジェクトの準備

## 2.1 プロジェクトの新規作成

プロジェクト:calculator 
ボード:m5stikkc
プラットフォームarduino

この時点のファイル構成

~~~
├─include
│      README
│
└─src
       main.cpp
~~~

## 2.1.1 main.cppに処理を追記します

~~~c:main.cpp
#include <M5StickC.h>
#include <calculator.h>

Calculator calc;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  M5.begin();
  Serial.printf("add %d\n", calc.add(1,3));
}

void loop() {
  // put your main code here, to run repeatedly:
}

~~~


# 3 テストの準備  

https://github.com/platformio/platformio-examples/tree/develop/unit-testing/calculator  

上記サンプルをベースにテストグループを作っていきます  

## 3.1 テスト対象のコード  

### 3.1.1 フォルダを追加します  
プロジェクト\lib\calculator\src  

### 3.1.2 ファイルを追加します
プロジェクト\lib\calculator\srcフォルダに  
下記２つのファイルを追加します  
calculator.cpp  
calculator.h  

### 3.1.3 追加ファイルに処理を記載します

~~~c:calculator.cpp  
/*
 Copyright (c) 2014-present PlatformIO <contact@platformio.org>
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
**/

#include <calculator.h>


int Calculator::add(int a, int b)
{
    return a + b + m_offset;
}

int Calculator::sub(int a, int b)
{
    return a - b + m_offset;
}

int Calculator::mul(int a, int b)
{
    return a * b;
}


int Calculator::div(int a, int b)
{
    return a / b;
}

int Calculator::offset(int a) {
    m_offset = a;
    return a;
}

~~~

~~~h:calculator.h  
/*
 Copyright (c) 2014-present PlatformIO <contact@platformio.org>
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
**/

#ifndef CALCULATOR_H
#define CALCULATOR_H

class Calculator {
private:
    int m_offset;

public:
    Calculator() {}
    int add (int a, int b);
    int sub (int a, int b);
    int mul (int a, int b);
    int div (int a, int b);
    int offset(int a);
};

#endif

~~~

## 3.2 テスト準備のコード  

### 3.2.1 フォルダを追加します  
プロジェクト\test\test_desktop  

### 3.2.2 ファイルを追加します
プロジェクト\test\test_desktopフォルダに  
下記２つのファイルを追加します  
test.cpp
test_group.cpp

### 3.2.3 test.cppの内容  
~~~c:test.cpp
/*
 Copyright (c) 2014-present PlatformIO <contact@platformio.org>
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
**/

#include <calculator.h>
#include <unity.h>
#include "unity_fixture.h"

extern void test_runner(void);

static Calculator calc;

int main(int argc, char **argv) {

    UNITY_BEGIN();

    /* suite */
    test_runner();

    UNITY_END();

    return 0;
}
~~~

### 3.2.4 test_group.cppの内容  

~~~c:test_group.cpp
#include <calculator.h>
#include <unity.h>
#include "unity_fixture.h"

static Calculator calc;

TEST_GROUP(Calc);

TEST_SETUP(Calc)
{
  calc.offset(0);
  //This is run before EACH TEST
  //Counter = 0x5a5a;
}

TEST_TEAR_DOWN(Calc)
{
    calc.offset(0);
}

TEST(Calc, add)
{
  //All of these should pass
  TEST_ASSERT_EQUAL(32, calc.add(25, 7));
  calc.offset(1);
  TEST_ASSERT_EQUAL(32, calc.add(25, 6));
}

TEST(Calc, sub)
{
  //All of these should pass
  TEST_ASSERT_EQUAL(18, calc.sub(25, 7));
  calc.offset(1);
  TEST_ASSERT_EQUAL(19, calc.sub(25, 7));
}

TEST(Calc, mul)
{
  //All of these should pass
  TEST_ASSERT_EQUAL(175, calc.mul(25, 7));
  TEST_ASSERT_EQUAL(31, calc.mul(25, 7));
}

TEST(Calc, div)
{
  //All of these should pass
  TEST_ASSERT_EQUAL(3, calc.div(25, 7));
  TEST_ASSERT_EQUAL(31, calc.div(25, 7));
}

TEST_GROUP_RUNNER(Calc)
{
  RUN_TEST_CASE(Calc, add);
  RUN_TEST_CASE(Calc, sub);
  RUN_TEST_CASE(Calc, mul);
  RUN_TEST_CASE(Calc, div);
}

void test_runner(void) {
  RUN_TEST_GROUP(Calc);
}
~~~

#### 3.2.2.1 test_group.cppの補足説明  
TEST_SETUP(Calc)  
テスト前に実施  

TEST_TEAR_DOWN(Calc)  
テスト後に実施  

TEST(Calc, add)  
足し算のテスト  

TEST(Calc, sub)  
引き算のテスト  

TEST(Calc, mul)  
掛け算のテスト  

TEST(Calc, div)  
割り算のテスト  

## 3.3 unityライブラリ  

### 3.3.1 unity_fixtureファイルをコピーします

https://github.com/ThrowTheSwitch/Unity/tree/master/extras/fixture/src
の  
unity_fixture.c  
unity_fixture.h  
unity_fixture_internals.h  
３ファイルを  
\test\test_desktopフォルダにコピーします  

### 3.3.2 unity_memoryファイルをコピーします

https://github.com/ThrowTheSwitch/Unity/tree/master/extras/memory/src
の  
unity_memery.c  
unity_memory.h  
２ファイルを  
\test\test_desktopフォルダにコピーします  

## 3.4 プロジェクトの全体構成

最終的には下記のようになります

~~~
├─include
│      README
│
├─lib
│  │  README
│  │
│  └─calculator
│      └─src
│              calculator.cpp
│              calculator.h
│
├─src
│      main.cpp
│
└─test
    │  README
    │
    └─test_desktop
            test.cpp
            test_group.cpp
            unity_fixture.c
            unity_fixture.h
            unity_fixture_internals.h
            unity_memery.c
            unity_memory.h
~~~


# 3 plafform.iniの準備

```diff
[env:m5stick-c]
platform = espressif32
board = m5stick-c
framework = arduino
monitor_speed = 115200
lib_deps = 
	M5StickC
+
+[env:native]
+platform = native
+test_ignore = test_embedded
```


# 4 テスト実行  

## 4.1 テスト実行のメニュー選択

Native  
Advanced  
Test  
でテスト実行

![menu.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693886/06c38745-cac1-f4d0-d413-c82cec503d75.gif)


## 4.2 テストの流れ

@startuml

start

:main
:UNITY_BEGIN
note:テストの開始

:test_runnner
:RUN_TEST_GROUP

:TEST_GROUP_RUNNER
note:テストの登録

partition 足し算 {
:TEST_SETUP(Calc)
:TEST(Calc, add)
:TEST_TEAR_DOWN(Calc)
}

partition 引き算 {
:TEST_SETUP(Calc)
:TEST(Calc, sub)
:TEST_TEAR_DOWN(Calc)
}

partition 掛け算 {
:TEST_SETUP(Calc)
:TEST(Calc, mul)
:TEST_TEAR_DOWN(Calc)
}

partition 割り算 {
:TEST_SETUP(Calc)
:TEST(Calc, div)
:TEST_TEAR_DOWN(Calc)
}

:UNITY_END
note:テストの終了

@enduml

## 4.3 テストの結果

掛け算と割り算を故意に間違えているのが  
FALSEとなっています GOOD!!  

![test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693886/9cb889f0-0310-b867-4de0-5ddcdbf00206.gif)


# 5 cppcheck実行
良く忘れるのでcppcheck実施方法も記録しておきます

Native  
Advanced  
Check  
でcppcheck実行

## 5.1 cppcheckの結果
![check.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693886/a17c8593-6194-b7d4-7340-0ba9b79a0dcf.gif)