Help us understand the problem. What is going on with this article?

Cpputestでモックなしではテストできないようなシステム関数を置き換えてみました。

More than 1 year has passed since last update.

ずっと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のようなシステム関数をモック化できるのはありがたい。

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away