6

作業中の低レベルのネットワーク コードに単純なオブジェクト モデルを使用します。ここでは、構造体ポインターがメソッドのふりをしている関数に渡されます。私は、せいぜいまずまずの C/C++ 経験を持つコンサルタントによって書かれたこのコードのほとんどを継承し、合理的な構造に似たものにコードをリファクタリングしようと夜遅くまで過ごしました。

ここで、コードを単体テストにかけたいと思いますが、選択したオブジェクト モデルを考慮すると、オブジェクトをモックする方法がわかりません。以下の例を参照してください。

サンプル ヘッダー (foo.h):

#ifndef FOO_H_
#define FOO_H_

typedef struct Foo_s* Foo;
Foo foo_create(TcpSocket tcp_socket);
void foo_destroy(Foo foo);
int foo_transmit_command(Foo foo, enum Command command);

#endif /* FOO_H_ */

サンプルソース (foo.c):

struct Foo_s {
    TcpSocket tcp_socket;
};

Foo foo_create(TcpSocket tcp_socket)
{   
    Foo foo = NULL;

    assert(tcp_socket != NULL);

    foo = malloc(sizeof(struct Foo_s));
    if (foo == NULL) {
        goto fail;
    }
    memset(foo, 0UL, sizeof(struct Foo_s));

    foo->tcp_socket = tcp_socket;

    return foo;

fail:
    foo_destroy(foo);
    return NULL;
}

void foo_destroy(Foo foo)
{
    if (foo != NULL) {
            tcp_socket_destroy(foo->tcp_socket);
            memset(foo, 0UL, sizeof(struct Foo_s));
            free(foo);
    }
}

int foo_transmit_command(Foo foo, enum Command command)
{
    size_t len = 0;
    struct FooCommandPacket foo_command_packet = {0};

    assert(foo != NULL);
    assert((Command_MIN <= command) && (command <= Command_MAX));

    /* Serialize command into foo_command_packet struct */
    ...

    len = tcp_socket_send(foo->tcp_socket, &foo_command_packet, sizeof(foo_command_packet));
    if (len < sizeof(foo_command_packet)) {
            return -1;
    }
    return 0;
}

上記の例では、ユニット テストで"foo_transmit_command"を使用できるようにTcpSocketオブジェクトをモックしたいと考えていますが、継承なしでこれを行う方法がわかりません。本当に必要でない限り、vtables を使用するようにコードを再設計したくありません。これには、嘲笑するよりも良いアプローチがあるのではないでしょうか?

私のテストの経験は主に C++ から来ています。経験豊富なテスターからのアドバイスをいただければ幸いです。

編集:
リチャード・クワークが指摘したように、私がオーバーライドしたいのは実際には「tcp_socket_send」への呼び出しであり、テストをリンクするときにライブラリから実際の tcp_socket_send シンボルを削除せずに実行することをお勧めします。同じバイナリ。

私は、この問題に対する明確な解決策はないと考え始めています..

4

7 に答える 7

3

マクロを使用して、 の実際の実装とダミーの実装を再定義tcp_socket_sendtcp_socket_send_moc、リンクすることができます。 の適切な場所を慎重に選択する必要があります。tcp_socket_sendtcp_socket_send_moc

#define tcp_socket_send tcp_socket_send_moc
于 2008-10-31T12:02:44.633 に答える
2

TestDept をご覧ください: http://code.google.com/p/test-dept/

これは、関数の代替実装 (スタブなど) を持つ可能性を提供し、使用する関数の実装を実行時に変更できるようにすることを目的としたオープン ソース プロジェクトです。

これはすべて、プロジェクトのホームページで非常にうまく説明されているオブジェクト ファイルをマングリングすることによって達成されます。

于 2009-06-10T14:31:45.367 に答える
1

または、組み込みソフトウェアにTestApe TestApeユニットテストを使用することもできます。これは可能ですが、Cのみであることに注意してください。このようになります->

int mock_foo_transmit_command(Foo foo, enum Command command) {
  VALIDATE(foo, a);
  VALIDATE(command, b);
}

void test(void) {
  EXPECT_VALIDATE(foo_transmit_command, mock_foo_transmit_command);
  foo_transmit_command(a, b);
}
于 2011-12-01T17:55:44.360 に答える
0

マクロを使用してtcp_socket_sendを調整するのが適切です。ただし、モックは1つの動作しか返しません。または、モック関数にいくつかの変数を実装し、各テストケースの前に異なる方法で設定する必要があります。もう1つの方法は、tcp_socket_sendをファンクションポイントに変更することです。そして、それを異なるテストケースの異なるモック関数に向けます。

于 2010-12-09T09:54:59.957 に答える
0

どのOSを使用していますか?GNU/Linux で LD_PRELOAD をオーバーライドできると思います:このスライドは役に立ちそうです。

于 2008-10-31T11:39:39.630 に答える
0

何を達成したいのかわからない。

すべての foo_* 関数を関数ポインター メンバーとして追加できますが、Cstruct Foo_sには暗黙的なポインターがないため、オブジェクトへのポインターを明示的に渡す必要がありますthis。ただし、カプセル化とポリモーフィズムが得られます。

于 2008-10-31T10:42:40.337 に答える