2

同じ C ファイル内にあるヘルパー メソッドをスタブ化する方法を探しています。ソースファイルを変更せずにこれを行う方法はありますか? メソッドを#defineメソッドに置き換えるという意味で何か考えていたのですが、これはメソッドの名前を変更することになると思いますbb_stubb

使用例を次に示します。

#include "file.h"

a(){
    b();
}

b(){
}

テスト フレームワークを作成しようとしていますが、フレームワークとスタブの定義を含む 1 つのファイルのみをユーザーに含めてもらいたいと考えています。

ありがとう。

4

4 に答える 4

3

あなたの質問を完全に理解しているかどうかわかりません。

それ以外のルーチンを呼び出したい場合はb、コンパイル時に次のように実行できます。

a() {
#ifdef USE_STUB
    b_stub();
#else
    b();
#endif
}

または、常に呼び出しbたいbが別の動作をしたい場合は、コンパイル時に次のように実行できます。

a() {
    b():
}
b() {
#ifdef USE_STUB
    printf( "I am in the stub version of b()\n" );
#else
    printf( "I am in the real version of b()\n" );
#endif
}

または、実行時に同様のことを実行できます (ここでは簡単にするためにグローバル変数を使用しています)。

a() {
    extern int _use_stub;
    if( _use_stub ) {
        b_stub();
    } else {
        b();
    }
}

また

a() {
    b();
}
b() {
    extern int _use_stub;
    if( _use_stub ) {
        printf( "This is the stub code\n" );
    } else {
        printf( "This is the real code\n" );
    }
}

コンパイル時の例では、ヘッダー ファイルまたは Makefile 定義を変更することで前後に切り替えることができます。実行時の例を使用すると、コマンド ライン オプション、環境変数、ユーザー設定ペインなどを使用して切り替えることができます。

于 2011-02-03T02:43:42.390 に答える
1

私に役立つ解決策を見つけました。おそらくあなたにも役立つでしょう。

MACROS を単独で使用しても、これまでのところしか得られません。関数でテストを実行したいが、MACROS を使用してさまざまな方法でスタブ化する場合、コードを何度か再構築して各条件を個別に実行する必要があります。これは自動化が難しいため、さまざまなシンボルを定義し、コードを再構築して結果を集計するバッチ スクリプトが必要です。

ただし、MACROS を使用して、スタブ化する各関数の関数ポインターを定義する場合は、テストするターゲット コードにいくつかのマイナーな変更を加えることができると仮定すると、実行可能なソリューションが得られます。

次の例は、次の影響を強く受けています。

  1. http://eradman.com/posts/tdd-in-c.html
  2. http://locklessinc.com/articles/mocking/
  3. http://www.embedded.com/design/programming-languages-and-tools/4007177/2/Doing-C-code-unit-testing-on-a-shoestring-Part-1-The-basics-and-道具

この例では、MUT はモジュール アンダー テストを表します。

mut.h、mut.c、test_mut.h、test_mut.c の 4 つのファイルがあるとします。また、これをビルドするときにシンボル UNIT_TEST を定義できると仮定しましょう。

mut.h には、公的にアクセス可能なすべての関数が含まれます。この例では何もないので、忘れておきましょう。

それでは、mut.c のバージョンから始めましょう。

#include <cstdbool>
#include "mut.h"

static bool foo(int baz);
static bool bar(int baz);

static bool foo(int baz)
{
    bool ret = false;

    if(bar(baz))
    {
        //do something
        ret = true;
    }
    else
    {
        ret = false;
    }
    return ret;
}

static bool bar(int baz)
{
    //Black box mystery / Don't care
}

すでに単体テスト済みの bar があるとしましょう。それは正常に動作します。ここで、foo をテストしたいのですが、bar を適切に実行するために必要なすべてをセットアップする気はありません。そのため、バーをスタブアウトする必要があります。

それでは、新しいヘッダー test_mut.h を含めましょう。とりわけ、test_mut.h には次のようなものがあります。

#ifdef UNIT_TEST
...

//Convert every static definition into an extern declaration.
#define static extern

bool bar_mock_true (int baz);
bool bar_mock_false(int baz);
bool bar_real      (int baz);

extern bool(*bar_ptr)(int baz);
#define bar bar_ptr

...
#endif

ご覧のとおり、スタブ/モックまたは実際のバー関数を指すことができる新しい関数ポインターを定義しました。このヘッダーは test_mut.c にも含まれるため、スタブ化された関数を test_mut.c 内で定義できるようになりました。mut.c 内にある必要はありません。

mut.c を少し変更する必要があります。

mut.c は "test_mut.h" をインクルードする必要があり、単体テスト中は bar() の標準宣言を無効にする必要があり、関数の定義を bar_real() に変更する必要があります。

...
#include "test_mut.h"
...
#ifdef UNIT_TEST
static bool bar(int baz);
#endif

...

#ifdef UNIT_TEST
static bool bar_real(int baz)
#else
static bool bar(int baz)
#endif
{
    //Black box mystery / Don't care
}

スタブ化する必要があるすべての関数には、同様の #ifdef と、宣言と定義の周りの名前変更が必要です。したがって、テスト対象のコードは、残念ながら少し雑然とする必要があります。

これで、test_mut.c は次のようにコードを実行できるようになりました。

#include <cstdbool>
#include "test_mut.h"

...

UT_STATIC void test_foo(void)
{
    int baz = 0;
    extern bool foo(int baz);
    //Test Case A
    bar_ptr = bar_mock_true;
    TEST_ASSERT(foo(baz), "Condition A");

    //Test Case B
    bar_ptr = bar_mock_false;
    TEST_ASSERT(!foo(baz), "Condition B");
}

bool bar_mock_true(int baz)
{
    return true;
}

bool bar_mock_false(int baz)
{
    return false;
}

以下は、私のソース ファイルの完全なリストです。ここで軽量のテスト ハーネスを作成しました。コンパイルして IAR 組み込みワークベンチ コンパイラで実行し (他では試していません)、次の出力を生成します。

.. テスト実行: 2

mut.c

#include <cstdbool>

#include "mut.h"
#include "test_mut.h"

static bool foo(int baz);

#ifndef UNIT_TEST
static bool bar(int baz);
#endif

static bool foo(int baz)
{
    bool ret = false;

    if(bar(baz))
    {
        //do something
        ret = true;
    }
    else
    {
        ret = false;
    }
    return ret;
}

#ifdef UNIT_TEST
static bool bar_real(int baz)
#else
static bool bar(int baz)
#endif
{
    //Black box mystery / Don't care
}

test_mut.h

#ifdef UNIT_TEST
#ifndef _TEST_MUT_H
#define _TEST_MUT_H

//Handle to your test runner
void test_mut(void);

//Track the number of test cases that have executed
extern int tests_run;

//Convert every static definitions into extern declarations.
#define static extern

//An alternate definition of static for the test barness to use
#define UT_STATIC static

bool bar_mock_true   (int baz);
bool bar_mock_false  (int baz);
bool bar_real        (int baz);

extern bool(*bar_ptr)(int baz);

#define bar bar_ptr

//Test Macros
#define TEST_FAIL(name)                                                           \
do                                                                                \
{                                                                                 \
    printf("\nTest \"%s\" failed in %s() line %d\n", (name), __func__, __LINE__); \
} while(0)

#define TEST_ASSERT(test_true,test_name)                                          \
do                                                                                \
{                                                                                 \
    tests_run++;                                                                  \
    if(!(test_true))                                                              \
    {                                                                             \
        TEST_FAIL(test_name);                                                     \
    }                                                                             \
    else                                                                          \
    {                                                                             \
        printf(".");                                                              \
    }                                                                             \
} while(0)

//... Insert any other macro instrumentation you may need...

#endif // _TEST_MUT_H
#endif // UNIT_TEST

test_mut.c

#ifdef UNIT_TEST

#include <cstdbool>
#include <cstdio>
#include "test_mut.h"
#include "mut.h"

UT_STATIC void test_foo(void);

int tests_run = 0;

inline UT_STATIC void test_report(void);

void test_mut(void) {
    //call your setup function(s)
    test_foo();
    //call your teardown function(s)

    test_report();
}

inline UT_STATIC void test_report(void)
{
    printf("\nTests Run: %d\n", tests_run);
}

void main(void)
{
    test_mut();
}

//Setup the function pointer for bar, by default it will point to the real
//bar function, and not a stub.
bool(*bar_ptr)(int baz) = bar_real;

UT_STATIC void test_foo(void)
{
    int baz = 0;
    extern bool foo(int baz);

    //Test Case A
    bar_ptr = bar_mock_true;
    TEST_ASSERT(foo(baz), "Condition A");

    //Test Case B
    bar_ptr = bar_mock_false;
    TEST_ASSERT(!foo(baz), "Condition B");
}

bool bar_mock_true(int baz)
{
    return true;
}

bool bar_mock_false(int baz)
{
    return false;
}

#endif
于 2014-12-03T17:43:32.497 に答える
0

これがエンドユーザーコードではなくフレームワークコードにある場合は、次のようなことができます

#ifndef UNIT_TEST_FUNC_B
b()
{
}
#endif

B で単体テストを実行する場合は、UNIT_TEST_FUNC_B を定義し、別のモジュールにスタブ コードを含めます。

テストコードを同じモジュールに保持したい場合は、これを行います

#ifndef UNIT_TEST_FUNC_B
b()
{
}
#else
b()
{
// test code goes here
}
#endif

異なるテストに対して異なる関数をスタブ化できるように、定義に一意の名前を使用しました。

于 2011-02-03T02:45:50.410 に答える
0

ソースを変更する必要がありますが、それほど多くはありません。

これを試して

#define b() stub_b()

a(){
    b(); 
   }  

(b)()
{

}

メソッド b() の呼び出しは stub_b() に置き換えられ、b() の定義は変更されません。:)

于 2011-02-03T12:19:49.980 に答える