ずっとgoogle mockしか知らなかったのですが、最近C言語(≠C++言語)周りを復習して、今更ですがCpputestがあることを知りました。google mockは随分更新されていないようだし、これからはCpputestなのかな?と入門してみました。
「Linuxのシステム関数(例:statvfs)をモックにしたい」が実現できました。
セットアップ
cppUTest のインストールを拝見してインストールしました。
私は、/usr/localにはインストールせず、$HOME/.localにインストールしています(好みです)。
インストール
$ cd ~/.local/src
$ git clone git://github.com/cpputest/cpputest.git
$ cd cpputest
$ cd cpputest_build
$ autoreconf .. -i
$ ../configure --prefix=$HOME/.local
$ make -j4
$ make install
モックを使う目的(今回)
sys/statvfs.h内のstatvfs()関数をモックに置き換えて、コンピュータのディスク容量を入力とする関数を色々テストしたい、というケースを試してみました。
このように、自分(プログラマ)ではどうすることもできない情報を入力とする関数のテストは、モックを使わないとテストができません。
例(テスト対象)
テスト対象コード
# include "target.h"
# include <sys/statvfs.h>
# include <stdio.h>
# include <errno.h>
# include <sys/types.h>
/*
ディスクの残量を取得する
*/
ulong get_disk_space(const char * const disk)
{
ulong ds = -1;
struct statvfs stat;
// 思ったような試験をするためには、制御可能なstatvfsを使う必要がある
if (statvfs(disk, &stat) == -1) {
perror(disk); // 例えば本物のstatvfsならこのパスはまず通らない
} else {
ds = stat.f_bavail * stat.f_blocks;
}
return ds;
}
やってみた
テストコード(メイン)
# include "CppUTest/CommandLineTestRunner.h"
# include "CppUTestExt/MockSupport.h"
# include <sys/statvfs.h>
# include "target.h"
// テストグループを定義
TEST_GROUP(TestFuncGroup) {
// 各テストケースの実行直前に呼ばれる仮想メソッド
TEST_SETUP() {
}
// 各テストケースの実行直後に呼ばれる仮想メソッド
TEST_TEARDOWN() {
mock().checkExpectations();
mock().removeAllComparatorsAndCopiers();
mock().clear(); // Mock資源をクリア
}
};
class StatvfsCopier : public MockNamedValueCopier
{
public:
virtual void copy(void* out, const void* in)
{
struct statvfs *outp = (struct statvfs*)out;
const struct statvfs *inp = (const struct statvfs*)in;
outp->f_blocks = inp->f_blocks;
outp->f_bavail = inp->f_bavail;
}
};
// テストを実行するメソッド
TEST(TestFuncGroup, Test1)
{
#define EXPECT_BLOCKS 12345
#define EXPECT_BAVAIL 67890
/* setup */
StatvfsCopier copier;
mock().installCopier("StatvfsBuf", copier);
struct statvfs out;
out.f_blocks = EXPECT_BLOCKS;
out.f_bavail = EXPECT_BAVAIL;
mock().expectOneCall("statvfs")
.withOutputParameterOfTypeReturning("StatvfsBuf", "buf", &out)
.andReturnValue(0);
/* test */
ulong ds = get_disk_space(".");
CHECK_EQUAL(EXPECT_BLOCKS*EXPECT_BAVAIL, ds);
}
int main(int argc, char **argv) {
// テストランナー
return RUN_ALL_TESTS(argc, argv);
}
statvfsを置き換えるモック
# include <sys/statvfs.h>
# include "target.h"
# include "CppUTestExt/MockSupport_c.h"
int statvfs(const char *path, struct statvfs *buf) {
mock_c()->actualCall("statvfs")
->withOutputParameterOfType("StatvfsBuf", "buf", buf);
return mock_c()->intReturnValue();
}
メモリリークは、new/deleteを使用する(C++)ケースだけのよう。malloc/freeは検出してくれず。それでも、statvfsのようなシステム関数をモック化できるのはありがたい。