🧪

Arduinoのユニットテスト(Unit testing)をやってみた

2022/04/10に公開

はじめに

Arduinoのライブラリを作ろうと思い、作っていたのですが、そういえばArduinoをはじめとする組み込みのテストってどうやるんだろうと思って調べ、ちょうど公式に以下のような記事がありました。
https://blog.arduino.cc/2021/04/09/test-your-arduino-projects-with-github-actions/
https://www.arduino.cc/reference/en/libraries/aunit/

これらの記事の中で、今回はAUnitを使ったユニットテストをやってみたいと思います。
日本語でAUnitの使い方を書いた記事は見つけられなかったので、素直にGithubにあるリファレンスを読んでやってみたいと思います。

https://github.com/bxparks/AUnit

順を追って書きますが、とりあえずAUnitの書き方を知りたい方は実際にテストコードを書くに飛んでください。

準備するもの(環境)

Javascriptなどと違って、AUnitはArduino系の開発ボードないしマイコンが必要になります。今回は以下の環境でAUnitを試しています。

  • 開発ボード: ESP32 dev board(ESP-WROOM-32)
  • IDE(Integrated Development Environment):Arduino IDE 1.8.19
  • OS : Windows 10 21H2

AUnitをインストールする

ライブラリマネージャーを使ってインストールするのが一番簡単だと思います。Arduino IDEのメニューバーの「ツール」を選択し、その中の「ライブラリを管理」を選択します。そしたら、ライブラリマネージャーが起動しますので、下の画像のように「検索をフィルタ…」に「AUnit」を入力してエンターを押すと、Brian T. ParkのAUnitが表示されると思います。バージョンを選んで、「インストール」ボタンを押してください。

テストコードを書いてみる

サンプル関数~division関数~

関数の第一引数を第二引数で割って答えを返す関数をテスト対象のサンプル関数とします。
入力の範囲は-20から20までとします。
つまり、以下のような関数です。

aunit_practice.ino
float division(int a, int b) {
  return a / b;
}

これだと、部分的にしか動かないんですが、これからテスト書いていくので、目を瞑ってください。

テストパターンを考える

では、テストパターンを考えます。今日やっつけでアップした記事[1]の図の通りにテスト項目を設定していきたいと思います。
テスト項目設定の流れ

テスト対象の入出力関係の書き出し

表1 入出力関係

入力(a) 入力(b) 出力
整数値 整数値 浮動小数点数

同値分割

入力の水準について、有効同値クラスと無効同値クラスに場合分けします。
今回はaとbの特定の組み合わせで無効同値になるパターンはないです。
分かりやすいように、有効同値には「○」を、無効同値には「×」を同値クラスの先頭に着けています。

表2 入力aの同値分割

入力(a) 同値クラス
-21 < a < 21 ○ 有効同値
a \le -21 × 無効同値
21 \le a × 無効同値

表3 入力bの同値分割

入力(b) 同値クラス
-21 < a < 0 ○ 有効同値
0 < a < 21 ○ 有効同値
a = 0 × 無効同値
a \le -21 × 無効同値
21 \le a × 無効同値

境界値分析

入力の水準の取り得る値の数が多いので、有効同値クラスと無効同値クラスの境界に絞ります。
数直線で表すと、下の図のようになります。
赤色が無効同値、青色が有効同値です。

境界値分析
境界値分析

テストパターン

今回は2種類の入力で、それぞれ境界値が4つと7つであり、すべての組み合わせの場合の数は28通りしかないので、すべてテストします。
この記事で紹介するのは一部です。
なお、無効同値の場合、aに不正な値があると戻り値の百の位を1に、bに不正な値があると戻り値の一の位を1にして返します。

表4 テストパターンの一例

入力(a) 入力(b) 期待される出力 同値クラス
-21 -21 101.0 × 無効同値
-21 -20 100.0 × 無効同値
-21 -1 100.0 × 無効同値
...略 ...略 ...略 ...略
-20 -1 20.0 ○ 有効同値
...略 ...略 ...略 ...略

実際にテストコードを書く

記事が長くなると読みにくくなるので、無効同値と有効同値を1つずつ抜き出してテストします。使うのは以下の表の2行です。

表5 テストコードに書くパターン

入力(a) 入力(b) 期待される出力 同値クラス
-21 -21 101.0 × 無効同値
-20 -1 20.0 ○ 有効同値

全て通るパターン

コード

aunit_practice.ino
#include <AUnit.h>
using namespace aunit;

// テスト対象の関数
double division(int a, int b) {
  double errorCode = 0;
  if (a < -20 || a > 20) errorCode += 100.0;
  if (b < -20 || b == 0 || b > 20) errorCode += 1.0;
  if (errorCode == 0) return a / b;
  else return errorCode;
}

// 無効同値のテスト(正常に通る)
test(error) {
  double result = division(-21,-21);
  assertEqual(101.0,result);
}

// 有効同値のテスト(正常に通る)
test(success) {
  double result = division(-20,-1);
  assertEqual(20.0,result);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  TestRunner::include("*"); //全てのテストを実行
  // TestRunner::include("error"); //無効同値のみ実行
  // TestRunner::include("success"); //有効同値のみ実行
}

void loop() {
  // put your main code here, to run repeatedly:
  TestRunner::run(); // テストの実行
}

結果

全て通る場合の結果
全て通る場合の結果

Test error passed.Test success passed.と表示されているうえ、TestRunner summaryに2passedと表示されています。うまく動いてそうです。

無効同値が通らないパターン

では、意図的に無効同値を通らないようにテストコードを書き換えてみようと思います。

コード

aunit_practice.ino
#include <AUnit.h>
using namespace aunit;

// テスト対象の関数
double division(int a, int b) {
  double errorCode = 0;
  if (a < -20 || a > 20) errorCode += 100.0;
  if (b < -20 || b == 0 || b > 20) errorCode += 1.0;
  if (errorCode == 0) return a / b;
  else return errorCode;
}

// 無効同値のテスト(正常に通らない)
test(error) {
  double result = division(-21,-21);
  assertEqual(100.0,result);
}

// 有効同値のテスト(正常に通る)
test(success) {
  double result = division(-20,-1);
  assertEqual(20.0,result);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  TestRunner::include("*"); //全てのテストを実行
}

void loop() {
  // put your main code here, to run repeatedly:
  TestRunner::run(); //テストの実行
}

結果

無効同値が通らない場合の結果
無効同値が通らない場合の結果

Test error failed.とTestRunner summaryに1 passed, 1 failedと出力されているのが確認できます。うまく動いてそうです。

まとめ

導入も書くのも簡単で、使ってみて、とても使いやすい印象でした。
出力がハイライトされないので、結果が読みづらいというのはありますが、仕方ないかなと思います。
今後、積極的に使っていきたいと思います。

脚注
  1. Akira Kashihara / テストパターンの決め方をまとめた(備忘録) https://zenn.dev/akira_kashihara/articles/08159feae4c6fe (2022-04-10閲覧) ↩︎

Discussion