作業中の低レベルのネットワーク コードに単純なオブジェクト モデルを使用します。ここでは、構造体ポインターがメソッドのふりをしている関数に渡されます。私は、せいぜいまずまずの 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 シンボルを削除せずに実行することをお勧めします。同じバイナリ。
私は、この問題に対する明確な解決策はないと考え始めています..