レガシーコードをC言語のTDD用フレームワーク『Fake Function Framework (fff)』ですっぽんぽんにする

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

    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を返すようにhogehoge2fake.returnvalに値を入れる。

    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);

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

    // 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