下に C 関数を使用するファサード パターンを実装しましたが、それを適切にテストしたいと考えています。
これらの C 関数を実際に制御することはできません。それらはヘッダーに実装されています。現在、本番環境では実際のヘッダーを使用し、テストではモック ヘッダーを使用するために #ifdef を使用しています。C関数のアドレスなどを上書きして、実行時にC関数を交換する方法はありますか? コード内の #ifdef を取り除きたいです。
下に C 関数を使用するファサード パターンを実装しましたが、それを適切にテストしたいと考えています。
これらの C 関数を実際に制御することはできません。それらはヘッダーに実装されています。現在、本番環境では実際のヘッダーを使用し、テストではモック ヘッダーを使用するために #ifdef を使用しています。C関数のアドレスなどを上書きして、実行時にC関数を交換する方法はありますか? コード内の #ifdef を取り除きたいです。
ifdef-selected ヘッダーが必要なのも奇妙です。テストするコードとモックは、テストするモジュールの正しいモックになるために、まったく同じ関数シグネチャを持つ必要があります。プロダクション コンパイルとテスト コンパイルの間で変更されるのは、リンカに指定する.oファイルだけです。
Bart の答えを拡張するには、次の簡単な例を考えてみましょう。
#include <stdio.h>
#include <stdlib.h>
int (*functionPtr)(const char *format, ...);
int myPrintf(const char *fmt, ...)
{
char *tmpFmt = strdup(fmt);
int i;
for (i=0; i<strlen(tmpFmt); i++)
tmpFmt[i] = toupper(tmpFmt[i]);
// notice - we only print an upper case version of the format
// we totally disregard all but the first parameter to the function
printf(tmpFmt);
free(tmpFmt);
}
int main()
{
functionPtr = printf;
functionPtr("Hello world! - %d\n", 2013);
functionPtr = myPrintf;
functionPtr("Hello world! - %d\n", 2013);
return 0;
}
出力
Hello World! - 2013
HELLO WORLD! - %D
Typemock Isolator++ を使用すると、不要な新しいレベルの間接化を作成することなく可能です。本番コードを変更することなく、テスト内で実行できます。次の例を検討してください。
コードに Sum 関数があります。
int Sum(int a, int b)
{
return a+b;
}
そして、テスト用にシグマに置き換えたいとします:
int Sigma(int a, int b)
{
int sum = 0;
for( ; 0<a ; a--)
{
sum += b;
}
return sum;
}
テストでは、使用する前に Sum をモックします。
WHEN_CALLED : 偽造したいメソッドを呼び出します。
ANY_VAL : モックが適用される引数の値を指定します。この場合、任意の 2 つの整数。
*DoStaticOrGlobalInstead : Sum に必要な代替動作。この例では、代わりに Sigma を呼び出します。
TEST_CLASS(C_Function_Tests)
{
public:
TEST_METHOD(Exchange_a_C_function_implementation_at_run_time_is_Possible)
{
void* context = NULL; //since Sum global it has no context
WHEN_CALLED(Sum (ANY_VAL(int), ANY_VAL(int))).DoStaticOrGlobalInstead(Sigma, context);
Assert::AreEqual(2, Sum(1,2));
}
};
* DoStaticOrGlobalInstead 代替メソッドを呼び出す代わりに、他のタイプの動作を設定することができます。例外をスローしたり、値を返したり、メソッドを無視したりできます...
例えば:
TEST_METHOD(Alter_C_Function_Return_Value)
{
WHEN_CALLED(Sum (ANY_VAL(int), ANY_VAL(int))).Return(10);
Assert::AreEqual(10, Sum(1,2));
}
実行時に関数を上書きするのは良い考えではないと思います。1 つには、実行可能セグメントが読み取り専用に設定されている可能性があり、そうでなくても、アセンブリが大きすぎると、別の関数のコードを踏むことになる可能性があります。
使用したい実装の一方と他方のセットに対して、関数ポインター コレクションのようなものを作成する必要があると思います。関数を呼び出すたびに、選択した関数ポインター コレクションから呼び出すことになります。これを行うと、関数ポインター構文を非表示にするプロキシ関数 (選択したセットから呼び出すだけ) を使用することもできます。