25

私たちはすべてのプロジェクトでninjectを使用していますが、ご存知のように、バインディングと自動バインディングの大きさが(ninject拡張機能を介して)制御が失われることがあるため、カーネルが実行時にすべてのタイプを解決できるかどうかをテストするのが難しい場合があります。 ) は高い。

それで、ここで私が求めているのは、すべてのモジュールとバインディングをロードした後、カーネルがすべてのタイプを解決できることをどうやって知ることができるかということです。ユニットテストはありますか?または、実行時にアプリケーションの受け入れテストを行うだけですか?どんな提案も素晴らしいでしょう:)

4

3 に答える 3

19

アプリケーション内のすべてのルートタイプをループし、コンテナー/カーネルからそれらを要求することにより、コンテナーの構成をテストする統合テストを記述します。コンテナからそれらを要求することにより、コンテナが完全なオブジェクトグラフを構築できることを確認できます。

ルートタイプは、コンテナから直接要求されるタイプです。ほとんどの型はルート型ではありませんが、オブジェクトグラフの一部です(アプリケーション内からコンテナにコールバックすることはめったにないため)。ルートタイプの作成をテストすると、構築プロセスを遅らせる可能性のあるプロキシ、ファクトリ、またはその他のメカニズムがない限り、そのルートタイプのすべての依存関係の作成をすぐにテストします。ただし、構築プロセスを遅らせるメカニズムは、他のルートオブジェクトを指し示します。それらを識別し、それらの作成をテストする必要があります。

ルートタイプごとにコンテナを呼び出して、1つの巨大なテストを行わないようにします。代わりに、(可能であれば)リフレクションを使用してすべてのルートタイプをロードし、それらを反復処理します。設定より規約を使用することで、1。新しいルートタイプごとにテストを変更する必要がなくなり、2。新しいルートタイプのテストを追加するのを忘れたときに不完全なテストを防ぐことができます。

ルートタイプがコントローラーであるASP.NETMVCの例を次に示します。

[TestMethod]
public void CompositionRoot_IntegrationTest()
{
    // Arrange
    CompositionRoot.Bootstrap();

    var mvcAssembly = typeof(HomeController).Assembly;

    var controllerTypes =
        from type in mvcAssembly.GetExportedTypes()
        where typeof(IController).IsAssignableFrom(type)
        where !type.IsAbstract
        where !type.IsGenericTypeDefinition
        where type.Name.EndsWith("Controller")
        select type;

    // Act
    foreach (var controllerType in controllerTypes)
    {
        CompositionRoot.GetInstance(controllerType);
    }
}

アップデート

セバスチャン・ウェーバーは私が答えたい興味深いコメントをしました。

コンテナに裏打ちされた(Func)またはコンテナで生成されたファクトリ(CastleのTyped Factory Facilityなど)を使用したオブジェクト作成の遅延はどうですか?あなたはその種のテストでそれらを捕まえることはありません。それはあなたに誤った安心感を与えるでしょう。

私のアドバイスは、すべてのルートタイプを確認することです。遅延して作成されるサービスは、実際にはルートタイプであるため、明示的にテストする必要があります。もちろん、これにより、構成の変更を注意深く監視し、すでに実施している設定より規約のテストではテストできない新しいルートタイプが追加されたことを検出したときにテストを追加する必要があります。DIとDIコンテナを使用すると、突然不注意になる可能性があるとは誰も言わなかったので、これは悪いことではありません。DIを使用するかどうかに関係なく、優れたソフトウェアを作成するには規律が必要です。

もちろん、作成を遅らせる登録が多数ある場合、このアプローチはかなり不便になります。その場合、アプリケーションの設計に問題がある可能性があります。これは、遅延作成の使用は例外であり、標準ではないためです。問題が発生する可能性のあるもう1つのことは、コンテナで未登録の登録をデリゲートFunc<T>にマッピングすることで解決できる場合です。() => container.GetInstance<T>()これは良さそうに聞こえますが、これにより、コンテナ登録を超えてルートタイプを探す必要があり、ルートタイプを見逃しやすくなります。遅延作成の使用は例外であるはずなので、明示的な登録を使用することをお勧めします。

また、構成を100%テストできない場合でも、構成のテストが役に立たないという意味ではないことに注意してください。ソフトウェアを100%自動的にテストすることはできません。自動的にテストできないソフトウェア/構成の部分には、特別な注意を払う必要があります。たとえば、テストできないパーツを手動のテストスクリプトに追加して、手動でテストすることができます。もちろん、手作業でテストしなければならないほど、うまくいかない可能性があります(そしてそうなるでしょう)ので、(すべてのソフトウェアで行うように)構成のテスト容易性を最大化するように努める必要があります。もちろん、テスト内容がわからない場合は、誤った安心感が得られますが、これも私たちの職業のすべてに当てはまります。

于 2012-09-11T19:29:57.537 に答える
4

それで、ここで私が求めているのは、すべてのモジュールとバインディングをロードした後、カーネルがすべてのタイプを解決できることをどうやって知ることができるかということです。ユニットテストはありますか?

モジュール内の各バインディングをループし、カーネルがそれらに対して何かを返すことができることを確認することによって、これをテストします。

[Test]
public void AllModuleBindingsTest()
{
    var kernel = new StandardKernel(new MyNinjectModule())
    foreach (var binding in new MyNinjectModule().Bindings)
    {
        var result = kernel.Get(binding.Service);
        Assert.NotNull(result, $"Could not get {binding.Service}");
    }
}
于 2017-11-16T15:14:49.833 に答える
1

オーウェンの答えに基づいていますが、それは私にとってはうまくいきませんでした。私がしなければならなかったのは、依存関係の新しいインスタンスを新しいカーネルインスタンスに渡すことでした。その時点で、Bindingsプロパティにデータが入力されました。

    [TestMethod]
    public void TestBindings
    {
        var dependencies = new MyDependencies();
        var kernel = new StandardKernel(dependencies);

        foreach (var binding in dependencies.Bindings)
        {
            kernel.Get(binding.Service);
        }
    }

またkernal.Get、サービスを解決できなかった場合、スローされたエラーが原因でテストが失敗し、メッセージに欠落しているバインディングが表示されるように使用することを選択しました

于 2019-01-15T18:47:13.543 に答える