Cutter

Cutter — C言語用単体テストフレームワーク

概要

void                setup                               (void);
void                teardown                            (void);
void                startup                             (void);
void                shutdown                            (void);
#define             cut_add_data                        (first_data_name, ...)

説明

Cutterは以下のような特徴をもったC用の単体テストフレームワークです。

  • 簡単に使えます。Cutterではテストプログラム中にCUTTER_DEFINE_TEST_STARTやCUTTER_DEFINE_TEST_ENDなどといった不思議なマクロを使う必要がありません。通常のプログラムと同じようにテストプログラムを書くことができます。ただし、プログラムが期待した通り動作していることを検証するためにcut_assert_XXX()を使う必要があります。

    #include <cutter.h>
    #include "my-stack.h"
    
    void
    test_my_stack (void)
    {
        MyStack *stack = my_stack_new();
    
        cut_assert_not_null(stack);
        cut_assert(my_stack_is_empty(stack));
        cut_assert_equal_int(0, my_stack_get_size(stack));
    
        my_stack_push(stack, 10);
        cut_assert(!my_stack_is_empty(stack));
        cut_assert_equal_int(1, my_stack_get_size(stack));
    
        my_stack_push(stack, 20);
        cut_assert_equal_int(2, my_stack_get_size(stack));
    
        cut_assert_equal(20, my_stack_pop(stack));
        cut_assert(!my_stack_is_empty(stack));
        cut_assert_equal_int(1, my_stack_get_size(stack));
    
        cut_assert_equal(10, my_stack_pop(stack));
        cut_assert(my_stack_is_empty(stack));
        cut_assert_equal_int(0, my_stack_get_size(stack));
    }
    
  • 簡素ですが有用な情報を出力をします。Cutterはデフォルトではテストが問題なく動いているときは静かに動きます。以下は、Cutter自身のテストの出力です。

    ...........................................................
    
    Finished in 0.213021 seconds
    
    59 test(s), 246 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 notification(s)
    

    Cutterはテストが1つパスしたことを示すためには「.」だけを出力し、最後にテスト結果の要約を出力します。Cutterは各テストの名前や何個検証したかなどの情報は表示しません。これは、テスト成功時にはこれらの情報は必要ないからです。

    Cutterは失敗時にはたくさんの情報を出力します。

    .....................F.....................................
    
    1) Failure: test_error
    <"Strange" == cut_test_result_get_test_name(result)>
    expected: <Strange!!!>
     but was: <dummy-error-test>
    test/test-cut-assertions.c:240: cut_assert_test_result()
    
    Finished in 0.223657 seconds
    
    59 test(s), 242 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 notification(s)
    

    上記の結果はCutterの自己テストにおかしな期待値を追加したために起きています。

    cut_assert_equal_string("Strange!!!", cut_test_result_get_test_name(result));
    

    おかしな検証はtest/test-cut-assertions.cの240行目に書かれていて、その行はcut_assert_test_result()関数内にあります。この関数はtest_errorテストから呼び出されています。cut_test_result_get_test_name(result)が"Strange!!!"を返すことを期待していますが、実際は"dummy-error-name"が返ってきています。このような情報を上記のCutterの出力から得ることができます。これはデバッグの手助けになるでしょう。

    Cutterの出力形式は実用的です。' but was:'はその上の'expected:'と並ぶようにインデントされています。これは期待値と実測値をパッと見て簡単に比較できるようにするためです。問題のあった行は「ファイル名:行: 関数」というように整形されています。これはEmacsと連携するためです。Emacsのcompilation-modeではこの形式が*compilation*バッファに表れると、next-errorコマンド(C-x `)で「ファイル名」の「行」へジャンプすることができます。これは問題行を素早く見つける手助けをします。

    Cutterは簡単にテストを書くことを支援するだけではなく、簡単にデバッグをできることも支援します。

詳細

setup ()

void                setup                               (void);

テストプログラム中でsetup()を定義していたら、cutterは各テストが実行される前に定義されたsetup()を呼びだします。


teardown ()

void                teardown                            (void);

テストプログラム中でteardown()を定義していたら、cutterは各テストの後に、たとえテストが失敗していた時でも、teardown()を呼びだします。


startup ()

void                startup                             (void);

テストプログラム中でstartup()を定義していたら、cutterは各テストケースが実行される前に定義されたstartup()を呼びだします。

0.8から


shutdown ()

void                shutdown                            (void);

テストプログラム中でshutdown()を定義していたら、cutterは各テストケースが実行された後に定義されたshutdown()を呼びだします。

0.8から


cut_add_data()

#define             cut_add_data(first_data_name, ...)

データ駆動テストで使うデータを追加します。

例:

#include <cutter.h>

void data_translate (void);
void test_translate (const void *data);

static const char*
translate (int input)
{
   switch(input) {
   case 1:
       return "first";
   case 111:
       return "a hundred eleven";
   default:
       return "unsupported";
   }
}

typedef struct _TranslateTestData
{
    char *translated;
    int input;
} TranslateTestData;

static TranslateTestData *
translate_test_data_new (char *translated, int input)
{
    TranslateTestData *data;

    data = malloc(sizeof(TranslateTestData));
    data->translated = strdup(translated);
    data->input = input;

    return data;
}

static void
translate_test_data_free (TranslateTestData *data)
{
    free(data->translated);
    free(data);
}

void
data_translate(void)
{
    cut_add_data("simple data",
                 translate_test_data_new("first", 1),
                 translate_test_data_free,
                 "complex data",
                 translate_test_data_new("a hundred eleven", 111),
                 translate_test_data_free);
}

void
test_translate(const void *data)
{
     const TranslateTestData *test_data = data;

     cut_assert_equal_string(test_data->translated,
                             translate(test_data->input));
}

first_data_name :

最初のデータ名。

... :

最初のデータとデータ破棄関数。続けて任意の数の「名前・データ・データ破棄関数(CutDestroyFunction)」の三つ組を指定します。

1.0.3から

参考

検証