😽

Ceedling を便利にする Tips

2022/05/30に公開

いろいろとつまづきながら Ceedling (Unity)を使っていますが、いろいろあって忘れそうなのでメモがてらこちらの記事につらつらと書いていこうと思います。

テストカバレッジ

作ったソースのどのくらいの範囲をテストでカバーできているか確認することができます。100%すべてのラインをテストする必要はないので、ストイックにテストをやりまくりたい人向けですね。実際やってみて、カバー率が高いと気持ちいいです。

前準備:

  • pip install gcovr
  • project.yml を編集(ceedling new したディレクトリに自動で作られるファイル)

project.yml は最後の plugins のところに gcov を追加します。

project.yml
:plugins:
  :load_paths:
    - "#{Ceedling.load_path}"
  :enabled:
    - stdout_pretty_tests_report
    - module_generator
    - gcov

あとは、以下のコマンドを実行するだけです。

$ ceedling gcov:all
$ ceedling utils:gcov
$ firefox build/artifacts/gcov/GcovCoverageResults.html

実行するテストの指定

ceedling でファイル名を指定するだけです。

$ ceedling test:test_my_code.c

CMock を使う

テスト対象の関数内から、別のライブラリや別ファイルにある関数を呼んでいる場合、対象のファイルをリンクしないといけないので大変です。また、テストパターンによってはその外部関数を実際にコールしたか気になりますね。CMock は偽物の関数を自動で用意して、ライブラリリンクなど不要になる仕組みです。モック、つまりは見せかけの物体、ということですね。使い方が複雑ですが、以下のことを覚えていればなんとかなると思います。

  1. モックにしたい関数を入れるヘッダファイルを作る。例として outsiders.h としましょう。
  2. テスト内で作ったヘッダファイルを mock_ をつけて、include します。#include "mock_outsiders.h"
  3. ceedling を実行すると build/test/mock の下にモック関数が生成されます。注意: ヘッダ内で extern された関数はモック化されません。
  4. モックがコールされたか知りたければ、テスト内でモック関数をコールします

例えば、fopen() がコールされたか知りたければ、以下のようなテストを書きます。

outsider.h
#include <stdio.h>
// このような標準ライブラリであれば、man に書いてあるものをコピーするだけでOK
FILE *fopen(const char *filename, const char *mode);
test_file_open.c
// mock_outsider.h は outsider.h から自動生成されます
#include "mock_outsider.h"
// テスト対象のヘッダ
#include "file_opener.h"
// setUp,tearDown省略
void test_file_opener(void){
	// char で動くか試してませんが、コールされるファイル名等を予想(Expect)して
	// NULL をリターンします。つまり fopen に失敗したときの挙動をチェックする
	// テストということになります。
	fopen_ExpectAndReturn("file.txt", "r", NULL);
	file_opener("file.txt");
}
file_opener.c
#include <stdio.h>
#include <file_opener.h>

void file_opener(char *filename){
    FILE *fp;
    if ((fp = fopen(filename, "r")) == NULL){
	    return;
    }
}
file_opener.h
extern file_opener(char *filename);

テストがコアダンプした場合

build/test/out の下にある実行ファイルがテストの実体なので、gdb で調べることができます。

$ gdb build/test/out/my_test.out
(gdb) run
0x0000555555559d83 in my_func (fuga = 0x0) at src/my_test.c:31
31		if(hoge(fuga->age))

Discussion