以前、こんな記事を書きました。

CMockは素晴らしいツールで、正直これがないとこの3ヶ月で心がへし折られていたと思う。しかし今日は、CMockに対向できるような素晴らしいツールを発見したので紹介。その名も、

FFF

ファイナルファンタジーではないが、魔法のようなツールだ。

FFFってなに

Fake Function Framework。ダミー関数を自動生成してくれる、『C言語』のためのツール。フェイク関数のフレームワークといいつつも、実際はスタブ関数やスパイ関数などなど、いろいろ生成するツールだ。

githubからダウンロードできる。

基本編

一番くわしいリファレンスは、githubのReadme.mdを以下はそれを参考に、いろいろと実験。

git clone git@github.com:meekrosoft/fff.git

xUnitフレームワークは、Unityを利用します。

はじめの一歩

定形の宣言。fff.hのなかに、すべてが集約されている。テストコードに以下を追記。

// Test_uhauha.c
#include "fff.h"
#include 
DEFINE_FFF_GLOBALS;

fff.hのなかで、memsetを利用しているようなので、string.hかが必要。

FFFでDummyする

まずは、ダミー関数を。hogehogeの実体をコンパイルすることなく、uhauha関数からhogehogeを呼び出す。

// uhauha.c
#include "uhauha.h"
#include "hogehoge.h"

void uhauha(void)
{
  hogehoge();
}

// hogehoge.h
int hogehoge(void)

void hogehoge(void)型を置き換えるには、テストコードに以下を追加する。

// Testuhauha.c
FAKE_VOID_FUNC(hogehoge);

これで、ダミー関数が宣言されて、コンパイルが通る。

これだけなのです!!魔法みたい!ウッハウハですね。

FFFでスタプする

hogehoge()をスタブ関数にします。必ず1を返すようにする。。

// uhauha.c
int uhauha2(void)
{
  return hogehoge2();
}

// hogehoge.h
int hogehoge2(void)

テストコードに以下を宣言。

// Testuhauha.c
FAKE_VALUE_FUNC(int, hogehoge2);

そして、実際のテストでは、1を返すようにhogehoge2_fake.return_valに値を入れる。

void test_uhauha2(void)
{
  hogehoge2_fake.return_val = 1;
  TEST_ASSERT_EQUAL(1, uhauha2() );
}

これで、hogehoge2は必ず1を返すフェイク関数ができる。

これだけなのです!!魔法みたい!ウッハウハですね。

FFFでスパイする

Spy関数も生成できるようです。

hogehoge3()に渡された値をスパイして、あとで結果をAssertします。

// uhauha.c
void uhauha3(int x)
{
  x++;
  hogehoge3(x);
}

// hogehoge.h
void hogehoge3(int);

テストコードはこちら。hogehoge3_fake.call_countで呼ばれた回数を、hogehoge3_fake.arg0_valで呼ばれた値を検証している。

// Testuhaha.c
FAKE_VOID_FUNC(hogehoge3,int);

void test_uhauha3(void)
{
  uhauha3(1);

  // check how many times hogehoge3 called.
  TEST_ASSERT_EQUAL(hogehoge3_fake.call_count, 1);
  // chech what value was given
  TEST_ASSERT_EQUAL(hogehoge3_fake.arg0_val, 2);
}

これで、hogehoge3をスパイできる。

これだけなのです!!魔法みたい!ウッハウハですね。

応用編

スタブとスパイの合わせ技

スタブしつつ、スパイの検証もできる。以下のように宣言。たとえば、

int hogehoge4(int);

をスパイしつつフェイクするには、以下を書く。

FAKE_VALUE_FUNC(int, hogehoge4,int);

シーケンス制御の検証

ほかにも、シーケンスの検証も可能。シーケンスのチェックこそがモッキングフレームワークなのだよ!詳細は省略。

カスタム関数を利用してフェイク、スパイする

登録した関数がコールされた時に、自分で作成した関数に飛ばすことができる。

 hogehoge5_fake.custom_fake = hogehoge_custom_fake;

のように宣言して

void hogehoge_custom_fake(){}

みたいに、自分が関数を用意する。プリプロセッサ接合部で置換するので、関数ポインタやリンク時の整合部を意識することなく、さくっと自前関数に飛ばせる。便利(・∀・)。

個人的最大の課題 データを渡す関数のモック

自分の扱っているコードは、メモリ獲得した構造体データを引数にして関数に渡すことがほとんど。こんな感じ。

//hogehoge.h
typedef struct hogehoge {
  int time;
  int status;
  int factor;
}HOGEHOGE;
void hogehoge5(HOGEHOGE *hogehoge);

//uhauha.c
void uhauha5(void)
{
  HOGEHOGE *hogehoge = (HOGEHOGE *)calloc(1,sizeof(HOGEHOGE));
  hogehoge->time   = 1;
  hogehoge->status = 2;
  hogehoge->factor = 3;

  hogehoge5(hogehoge);
}

こういうhogehoge5をチェックするには、自前のスパイ関数を作成して検証していた。fff.hを利用すれば検証がサクットできる。

HOGEHOGE last_hogehoge;
void hogehoge5_spy(HOGEHOGE *hogehoge)
{
  memcpy(&last_hogehoge,hogehoge,sizeof(HOGEHOGE));
  free(hogehoge);
}

void test_uhauha5(void)
{
  hogehoge5_fake.custom_fake = hogehoge5_spy;
  uhauha5();

  TEST_ASSERT_EQUAL( 1, last_hogehoge.time);
  TEST_ASSERT_EQUAL( 2, last_hogehoge.status);
  TEST_ASSERT_EQUAL( 3, last_hogehoge.factor);
}

感想

以前の記事で、『プリプロセッサ接合部』について書きました。

このfffは、『プリプロセッサスゲーΣ(゚Д゚ノ)』と思わせるツールでした。C言語のプリプロセッサの底力を垣間見たフレームワーク。レガシーC言語バンザイヽ(´ー`)ノ

CMockは便利だが、xUnitがUnityに限定されてしまう。それに対してこのfff.hは、移植性がとても高そうだ。評価してないけど、いろんなフレームワークでいっしょに利用できそう。C言語のフレームワークだが、extern “C"のテクニックをりようすることで、C++系のxUnitフレームワークでも利用可能。

CMockはRubyスクリプトでダミー関数を一気に生成することができる。

fffは必要なダミー関数を必要なだけ作成する点が異なる。自分の扱っているコードはテストがあるなんという金持ち環境ではなく、技術的負債で潰れかけているので、ねじ込むようにテストを書くにはCMockが必須う。

CMockだと、引数に構造体ポインタを渡す関数をうまくモックできなかった(やりかたがわからないだけかも)。そういう場合は、結局自分でモック関数を作成していた。しかし、fffを利用すればちょっと楽がデキそうた。

Reference