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

恐るべきレガシーコードの救世主になるか?!ドロドロ依存なモジュールたちを『CMock』ですっ裸にする | Futurismo

どんなコードに対しても、テストが簡単にかけるみたいな、素敵なことを夢想したわけだが、それは空虚な夢幻だった。ピーターパンだった。これを、仕事で実践しようとしたときに、現実は冷酷だった。

自動生成してもコンパイル通らない(´。・ω・。`)

結局ドロドロな手動ダミー関数地獄に落ちていき、忙殺されたのだった。今日は、そんな苦労のなかで見つけたTipsをメモします。

64bit対応

コンパイルオプションに以下のデファインを追加すればいいっぽい。

UNITY_INCLUDE_64

//を/* */に置き換え

そもそも、//を利用してコメントするとコンパイルエラー。。手強い。/* */に変換した。

UNITY_OUTPUT_CHARで標準出力関数を置き換え

Unityでは標準出力のために、putcharを利用している。

//-------------------------------------------------------
// Output Method
//-------------------------------------------------------

#ifndef UNITY_OUTPUT_CHAR
//Default to using putchar, which is defined in stdio.h above
#define UNITY_OUTPUT_CHAR(a) putchar(a)
#else
//If defined as something else, make sure we declare it here so it's ready for use
extern int UNITY_OUTPUT_CHAR(int);
#endif

自分の環境(vxWorks)では、putcharが使えなかった。printfは利用できるようだった。そこで、UNITY_OUTPUT_CHARを自分で定義しななおして、標準出力関数を置き換えた。

//-------------------------------------------------------
// Output Method
//-------------------------------------------------------

#define UNITY_OUTPUT_CHAR(a) printf("%c",(a))

#if 0
#ifndef UNITY_OUTPUT_CHAR
//Default to using putchar, which is defined in stdio.h above
#define UNITY_OUTPUT_CHAR(a) putchar(a)
#else
//If defined as something else, make sure we declare it here so it's ready for use
extern int UNITY_OUTPUT_CHAR(int);
#endif
#endif

インクルードファイルの挿入

OSのファイルを先頭に取り込まないと行けない場合がある。自分の場合は、仕事でvxWorksを利用しているので、“vxWorks.h”。Windowsだと、“Windows.h"みたいな。生成されたモックファイルたちにも、ソースの頭で読みこむ必要かあった。

cmock/lib/cmock_config.hの以下のパラメータにヘッダファイルを入れることで自動生成するものに挿入してくれる。

:includes\_h\_pre\_orig\_header  => nil,
:includes\_h\_post\_orig\_header => nil, 

:includes\_c\_pre_header       => nil, 

:includes\_c\_post_header      => nil 

たとえば、Cソースの頭にヘッダファイルを挿入したい場合は、

:includes\_c\_pre\_header  => \['hogehog\_internal.h'\] (ここは配列)

とする。すると、以下のように生成される。

#include <string.h>

#include <stdlib.h>

#include <setjmp.h>

#include “unity.h”

#include “cmock.h”

#include “hogehoge.h”

#include “CMock_hogehoge.h”

string.h、stdlib.h、setjmp.h、unity.h、cmock.hのあとに挿入される。自分の場合は、<string.h> の前に挿入したかったので、lib/cmock_generater.rbを直接編集をしてしまったorz。

とりあえず、hogehoge_internal.hを先頭に挿入して、あとはコンパイルエラーが出る度に、hogehoge_internal.hに場当たり的にいろいろ定義していった。

extern宣言している関数をモックする

ヘッダファイルにextern宣言されている関数はモックで自動生成されなかった。調べたら、ヘッダファイルにexternを書くことは、なんの意味もないらしい!(グローバル変数は別)

CMockの場合、デフォルトではextern宣言されている関数のモック作成はしないが、オプションを変更することで可能。

lib/cmock_config.rbの:treat_externs のオプションを:excludeら:includeに変更する。

=begin

:treat_externs            => :exclude,          

=end

:treat_externs            => :include,       

困るのが、extern宣言されている関数が複数箇所で宣言されている場合。これは妥協して手動でどちらかの関数を#if 0した。

extern宣言しているグローバル変数の扱い

グローバル変数の場合、extern宣言はヘッダファイルに書いて、実体はCソース書くことがルール。テストをするときに、テストをするときに、テストコードに実体を宣言して利用していたが、このグローバル変数の実体を自動生成してくれないかなとおもった。調べると、こんな記事を発見。

CMock / Discussion / CMock Forum:Mocking extern functions

これによると、自分のテストファイルに変数を宣言しなさいとある。はい、そうします。

自動生成されたモックと手動生成したモックの使い分け

とりあえず、接頭語をMock_とCMock_で分ける。

MakefileでどちらのCMock_(自動生成)かMock_(手動生成)のどちらを利用するかを選ぶ。ある関数は自動生成を利用したいけど、別関数は自前の関数を利用したいなんてこともある。

そのときは、callbackオプションを利用する。

:plugins                  => ['callback'],

これで、hogehoge()に対して、hogehoge_StubWithCallbackという関数が生成される。この関数に、自前のモック関数をセットすればよい。

例えば、Mock_hogehoge { return 2;}を使って、こんな風に書く。

#include "Mock_hogehoge.h"

TEST(hogehoge,second)
{
  hogehoge_StubWithCallback(Mock_hogehoge());
  TEST_ASSERT_EQUAL( 2, (hogehoge() + 1) );
}

しかし、この宣言をいちいち記述するのも面倒なので、よく呼ばれる関数は、ダミーかフェイクを自前で作ったほうがよいとおもった。(OSの関数はモックするべきでなかったとおもった)

モック関数としてではなくて、フェイク関数として使う場合

Ignore関数を呼ぶとよい。;pluginにignoreオプションを追加でモック関数が生成される。パラメータチェックや何回呼ばれたかのチェックがされなくなる。

:plugins                  => ['ignore'],

モードが2種類あり、呼ばれた回数はチェックする場合とそうでない場合。ignoreオプションで選ぶ。(args_and_call / args_only)

staticなし関数をかってにexternでみてる場合

ヘッダファイルに宣言されていないが、static宣言もない、他のファイルの関数を勝手に使ってる。これは、対処の方法がわからなかった。手動生成。

最後に

CMockは痒いところに手が届く、使い勝手がよいツールなので調べればいい手が見つかるかもしれない。Rakeをもう少し使いこなして、このへんの処理をやりたいことろだ。まあ、地獄に落ちつつも後先考えないやっつけ仕事でテスト環境はとりあえずできたので、よいのだけれども。