質問
プロファイラーのレポートでは、依存性注入を使用したモックベースのテストの結果が増えています。依存関係の多くは静的でしたが、メソッドを個別にテストするため、次の例のようにインスタンスメンバーに変更されます。
class ShortLivedThing {
IDependency1 dep1;
IDependency1 dep2;
IDependency1 dep3;
...
int TheRealData;
// Constructor used in production
public ShortLivedThing() {
dep1 = new Dep1(); dep2 = new Dep2(); dep3 = new Dep3();
}
// DI for testing
public ShortLivedThing(IDependency1 d1, IDependency2 d2, IDependency3 d3) {
dep1 = d1(); dep2 = d2(); dep3 = d3();
}
}
ほとんどの場合、依存関係には他の依存関係などがあります。これにより、テストの外でメソッド呼び出しが毎回行われる(ほとんど「静的」)オブジェクトのツリーがインスタンス化されます。各オブジェクトは非常に小さい(ほんの数個のポインター)が、ツリー効果によりこれはパフォーマンスの向上につながります。
私たちはそれについて何ができますか?
解決
適切な依存性注入フレームワークが提供できる機能を活用する必要があるように思えます。テスト/本番用に異なる構築ロジックを使用しないでください。
スプリングでは、シングルトン注入はコンテナの起動時にのみ実行されます。プロトタイプの注入は毎回行われます。配線されている場合は、ユニットテストを実行するたびに完全な配線も行われます。そのため、単体テストのプロファイリングは一般的には良いアイデアではありません。
使用しているシングルトンスコープが少なすぎ、プロトタイプスコープが多すぎますか? (プロトタイプ=毎回新しいインスタンス)
スプリングインジェクションの良い点は、スコーププロキシを使用できることです。つまり、オブジェクトグラフは次のようになります。
A Singleton
|
B Singleton
|
C Prototype (per-invocation)
|
D Singleton
|
E Session scope (web app)
|
F Singleton
各リクエストは、セッションごとにCの1つのインスタンスとEの1つのインスタンスのみを作成します。 A、B、D、およびFはシングルトンです。 webappでない場合、デフォルトではセッションスコープを持っていませんが、カスタムスコープを作成することもできます(ウィンドウデスクトップアプリケーションの場合、「ウィンドウ」スコープはクールに聞こえます)。ここでの手がかりは、「紹介」できることです。任意のレベルのスコープ、事実上、シングルトンオブジェクトの10層を持つことができ、突然スコープされたセッションのすべてが表示されます。 (これは、階層化されたアーキテクチャでいくつかの横断的機能を実装する方法に本当に革命をもたらすことができますが、それは別の話です)
これにより、DIモデル内で可能な限り最小限のオブジェクト作成が可能になります。
これはSpring for Javaですが、他の多くのDIフレームワークも同様の機能をサポートするはずです。おそらく最もミニマルなものではありません。
他のヒント
「DIコンストラクター」のみが必要だと思います。このコンストラクターは、テスト用および実動用に呼び出します。
class ShortLivedThing {
IDependency1 dep1;
IDependency1 dep2;
IDependency1 dep3;
...
int TheRealData;
public ShortLivedThing(IDependency1 d1, IDependency2 d2, IDependency3 d3) {
dep1 = d1; dep2 = d2; dep3 = d3;
}
}
この方法では、テストの外部でメソッド呼び出しが行われるたびにオブジェクトのツリーをインスタンス化する問題はありません。もちろん、本番環境では、参加しているオブジェクト自体とオブジェクトを正しく外接続する必要があります。これは良いことです。
要約:50%DI / 50%ハードコーディングではなく、100%DIに進みます。
テストが遅いことが懸念される場合は、テストを並行して実行してみてください。テストプロセスがプログラマに割り込まないようにしてください。
このプロセスを自動化:
- 誰かがチェックインしたら、リポジトリからビルドを作成します。
- このビルドでテストを実行します。
- E-チェックインした開発者に結果をメールで送信します。
最初のチェックインを実際のリポジトリに対して行わない方が良いでしょう。一時的なものにして、これからビルドを作成します。オプションで、パフォーマンステスト、スタイルチェックなどを実行し、これらを電子メールに含めることができます。これを行う場合、自動化プロセスに1つのステップを追加します。
- テストに合格した場合(およびオプションの条件が満たされた場合)、新しいコードを実際のリポジトリにマージします。
この方法により、遅いテストは問題になりません。また、開発者が自分のコードが何かを壊したか、期待したパフォーマンスを向上させたかを知る必要があるとき、彼女はチェックインし、彼女のために生成された電子メールを待ちます。
参照を渡すのはどうですか?
思いつくのは、すべての依存関係を1つの「コンテキスト」に入れることです。その後、すべてのインスタンス間で共有されるオブジェクト。これにより、パフォーマンスの問題が多少緩和されます。
.NETをターゲットにしている場合は、 Autofac をご覧ください。作成の側面を微調整するためのさまざまなスコープ(シングルトン、ファクトリー、コンテナー)、リソース使用量を抑えるための決定論的な廃棄、およびGeneratedFactoriesとラムダ式を使用してコンポーネントを構成し、リフレクションのコストを回避できます。