0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CMockで依存関数を置き換えてCの単体テストを動かす

0
Posted at

センサーを読むCの関数は、実機がないとテストしにくいことがあります。CMockを使うと、センサーの読み取りをモックに置き換え、好きな値で判定処理をテストできます。

CMockの役割

CMockは、Cのヘッダからモックを生成するツールです。依存関数の戻り値を指定し、呼び出し回数や引数を検証できます。公式の説明も参照してください。

今回は、テストの判定にUnity、モック生成にCMock、生成からビルド・実行までの管理にCeedlingを使います。UnityとCMockはCeedlingに同梱されます。

準備

Ruby 3とGCCを用意し、ruby・gem・gccをターミナルから実行できる状態にしてください。導入方法はCeedlingの公式手順にあります。

gem install ceedling -v 1.0.1 --no-document
mkdir cmock-sample
cd cmock-sample
mkdir src test

Windowsでは、プロジェクトを英数字だけのパスに作成してください。以下の5ファイルを作れば、添付ファイルなしで試せます。

この記事ではCeedling 1.0.1を指定しています。実行時の_1.0.1_も、使用するバージョンの指定です。

cmock-sample/
├── project.yml
├── src/
│   ├── sensor.h
│   ├── alarm.h
│   └── alarm.c
└── test/
    └── test_alarm.c

コードと設定

src/sensor.hは、依存関数の宣言です。今回はモックを使うため、sensor.cは不要です。

#ifndef SENSOR_H
#define SENSOR_H

int sensor_read(int channel);

#endif

src/alarm.hに、テスト対象の関数を宣言します。

#ifndef ALARM_H
#define ALARM_H

int alarm_is_hot(void);

#endif

src/alarm.cは、チャネル1の温度が30以上なら1、それ未満なら0を返します。

#include "alarm.h"
#include "sensor.h"

int alarm_is_hot(void)
{
    return sensor_read(1) >= 30;
}

test/test_alarm.cで、境界の前後を確認します。

#include "unity.h"
#include "alarm.h"
#include "mock_sensor.h"

void setUp(void) {}
void tearDown(void) {}

void test_below_threshold(void)
{
    sensor_read_ExpectAndReturn(1, 29);
    TEST_ASSERT_EQUAL_INT(0, alarm_is_hot());
}

void test_at_threshold(void)
{
    sensor_read_ExpectAndReturn(1, 30);
    TEST_ASSERT_EQUAL_INT(1, alarm_is_hot());
}

void test_above_threshold(void)
{
    sensor_read_ExpectAndReturn(1, 31);
    TEST_ASSERT_EQUAL_INT(1, alarm_is_hot());
}

sensor_read_ExpectAndReturn(1, 29)は、「引数1で1回呼ばれ、そのとき29を返す」という設定です。引数や呼び出し回数が合わなければ、テストは失敗します。

mock_sensor.hは自分で書きません。テストでこのヘッダをインクルードすると、CeedlingがCMockを使ってsensor.hからモックを自動生成します。

最後に、プロジェクト直下へproject.ymlを作成します。

---
:project:
  :build_root: build
  :use_mocks: TRUE
:paths:
  :test:
    - test/**
  :source:
    - src/**
  :include:
    - src/**
:cmock:
  :mock_prefix: mock_
:plugins:
  :enabled:
    - report_tests_pretty_stdout

実行

project.ymlがあるディレクトリで実行します。

ceedling _1.0.1_ test:all

Ceedlingがモック生成、コンパイル、リンク、テスト実行を行います。アプリケーション用のmain()も不要です。

Windows環境のRuby 3.4.11、GCC 14.2.0、Ceedling 1.0.1、CMock 2.6.0、Unity 2.6.1で実行し、3件すべて成功しました。出力の要点は次のとおりです。

OVERALL TEST SUMMARY
TESTED:  3
PASSED:  3
FAILED:  0
IGNORED: 0

失敗を検出できることも確認できます。alarm.cの>= 30を> 30に変えて再実行すると、温度30のテストがExpected 1 Was 0で失敗します。確認後は>= 30に戻してください。

このテストで確認するのは、指定したセンサー値に対する判定と、依存関数の呼び出しです。実際のセンサーや通信処理の動作は、別途確認します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?