12

C またはその他の手続き型プログラミング言語で大規模なアプリケーションを作成することにしたとします。次のような呼び出し依存関係を持つ関数があります。

A
|
+-------------+
|             |
B1            B2
|             |
+------+      +------+
|      |      |      |
C11    C12    C21    C22

明らかに、リーフ関数 C11、C12、C21、および C22 の単体テストは非常に簡単です。入力をセットアップし、関数を呼び出し、出力をアサートします。

しかし、B1、B2、および A の適切な単体テストを有効にするための適切な戦略は何ですか?

依存性注入B1は、 (B2また) 次のように宣言することを示唆していますか?

// Declare B1 with dependency injection for invoking C11 and C12.
int B1(int input, int (*c11)(int), int(*c12)(int));

しかし、呼び出しのレイヤーが多数ある場合、その戦略はスケーラブルではないようです。Aの宣言がどのようになるか想像してみてください。

int A(int input, int (*b1)(int, int (*)(int), int(*)(int)), 
                 int(*b2)(int, int (*)(int), int(*)(int)),
                 int (*c11)(int),
                 int (*c12)(int),
                 int (*c21)(int),
                 int (*c22)(int));

うん!もっと良い方法があるはずです。

モジュール性とメンテナンスの容易さを促進すると主張する DI やその他の同様のパターンが、実際にはコードの明瞭さを妨げ、単純なコーディングであるべきものを無意味な抽象化と複雑な間接化に複雑化させていると感じることがあります。

Perl や Ruby のような大規模な C のソフトウェア プロジェクトでは、単体テストはどのように処理されますか?

4

5 に答える 5

3

AB1とを呼び出すだけB2です。経営幹部レベルでは何も知る必要はありません。

テストの目的で、関数の異なるダミーバージョンをに注入することがB1できB2ます。AA

これによりA、構造全体が不要になり、各機能を個別にテストできるようになります。

于 2011-04-06T05:30:04.123 に答える
3

単体テストに DI のみが必要な場合は、リンカを使用して実行できます。

つまり、関数 B1 と B2 はヘッダーで宣言され、関数 A によって使用されるため、B 関数の実装はリンカーによって提供されます。単体テスト用に別の C ファイルを提供する必要があるだけです。いずれにせよ、単体テスト用に独自の makefile を持っている可能性があるため、これは大きな問題にはなりません。

実行時に動的な依存関係の解決が必要な場合は、関数ポインターにファクトリー パターン (関数ポインターを返す関数) を使用し、必要に応じてファクトリーからプルする必要があります。ファクトリは、グローバル コンテキストに応じて、どの関数を返すかを決定する場合があります。

于 2011-04-14T06:21:39.820 に答える
2

関数呼び出しの1つのパラメーターとなるc-structに依存関係を置くことができます。cでは、これは最初のパラメーターが常にファイルハンドルであるファイルAPIに似ています。

于 2011-04-03T06:10:04.453 に答える
0

私はこの質問が好きです。手続き型言語では少し注意が必要ですが、オブジェクト指向の世界からアイデアを借りることができると思います。オブジェクト指向の世界では、コンストラクターのオーバーロードを使用してDI作業の一部を処理することがよくあります。したがって、たとえば、通常どおりすべての依存関係を設定するデフォルトのコンストラクターを使用しますが、依存関係を挿入できる別のコンストラクターも使用します。

あなたは手続き型なので...関数のオーバーロードを使用してこれを処理できると思います。また、テストしているときは、Aを呼び出すときにB1とB2をモックアウトするだけで済みます。そのため、その目的のためにDIを単純化できます。言い換えれば、実際に単体テストにDIのみを使用している場合は、依存関係のツリー全体を注入する必要はなく、最初のレベルの依存関係のみを注入する必要があります...

だからAからあなたは持っているかもしれません...

int A(int input){
// create function point to b1 & b2 and call "return A(input, {pointer to b1},{pointer to b2})"
}

私の疑似コードを許してください。私がCをしたのは久しぶりです。

于 2011-04-03T05:45:54.240 に答える
0

DIなしでB1、B2、およびAの適切な単体テストを実行できます。リーフ関数と同様に、B1 と B2 には有効な入力と出力があり、A と同じようにそれらをテストします。B1 が C11 と C12 を内部で使用して単体テストを実行できるからといって、場合によってはそれらを注入する必要があるわけではありません。その柔軟性は必要ありません。

于 2011-04-14T15:04:20.010 に答える