36

ASP MVC V5 と属性ルーティングを使用するソリューションのテスト プロジェクトで、非常に単純なテストを行っています。属性ルーティングとMapMvcAttributeRoutesメソッドは、ASP MVC 5 の一部です。

[Test]
public void HasRoutesInTable()
{
    var routes = new RouteCollection();
    routes.MapMvcAttributeRoutes();
    Assert.That(routes.Count, Is.GreaterThan(0));
}

これにより、次の結果が得られます。

System.InvalidOperationException : 
This method cannot be called during the applications pre-start initialization phase.

このエラー メッセージに対する回答のほとんどは、ファイルでのメンバーシップ プロバイダーの構成に関係していweb.configます。このプロジェクトにはメンバーシップ プロバイダーもweb.configファイルもないため、他の理由でエラーが発生しているようです。テストを実行できるように、この「開始前」の状態からコードを移動するにはどうすればよいですか?

の属性の同等のコードは、が呼び出されたApiController後に正常に動作します。HttpConfiguration.EnsureInitialized()

4

4 に答える 4

19

最近、プロジェクトを ASP.NET MVC 5 にアップグレードしましたが、まったく同じ問題が発生しました。dotPeekを使用して調査したところ、コントローラ タイプのリストを期待するパラメータとしてMapMvcAttributeRoutesを持つ内部拡張メソッドがあることがわかりました。IEnumerable<Type>リフレクションを使用する新しい拡張メソッドを作成し、属性ベースのルートをテストできるようにしました。

public static class RouteCollectionExtensions
{
    public static void MapMvcAttributeRoutesForTesting(this RouteCollection routes)
    {
        var controllers = (from t in typeof(HomeController).Assembly.GetExportedTypes()
                            where
                                t != null &&
                                t.IsPublic &&
                                t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
                                !t.IsAbstract &&
                                typeof(IController).IsAssignableFrom(t)
                            select t).ToList();

        var mapMvcAttributeRoutesMethod = typeof(RouteCollectionAttributeRoutingExtensions)
            .GetMethod(
                "MapMvcAttributeRoutes",
                BindingFlags.NonPublic | BindingFlags.Static,
                null,
                new Type[] { typeof(RouteCollection), typeof(IEnumerable<Type>) },
                null);

        mapMvcAttributeRoutesMethod.Invoke(null, new object[] { routes, controllers });
    }
}

そして、これが私がそれを使用する方法です:

public class HomeControllerRouteTests
{
    [Fact]
    public void RequestTo_Root_ShouldMapTo_HomeIndex()
    {
        // Arrange
        var routes = new RouteCollection();

        // Act - registers traditional routes and the new attribute-defined routes
        RouteConfig.RegisterRoutes(routes);
        routes.MapMvcAttributeRoutesForTesting();

        // Assert - uses MvcRouteTester to test specific routes
        routes.ShouldMap("~/").To<HomeController>(x => x.Index());
    }
}

ここでの問題の 1 つは、内部RouteConfig.RegisterRoutes(route)で呼び出すことができないroutes.MapMvcAttributeRoutes()ため、代わりにその呼び出しを Global.asax ファイルに移動したことです。

もう 1 つの懸念は、上記の方法RouteCollectionAttributeRoutingExtensionsは内部的なものであり、いつでも削除できるため、このソリューションは脆弱である可能性があることです。予防的なアプローチは、変数が null かどうかを確認し、 null のmapMvcAttributeRoutesMethod場合は適切なエラー/例外メッセージを提供することです。

注:これは ASP.NET MVC 5.0 でのみ機能します。ASP.NET MVC 5.1 の属性ルーティングに大幅な変更があり、mapMvcAttributeRoutesMethodメソッドは内部クラスに移動されました。

于 2013-11-28T14:56:17.747 に答える
5

まあ、それは本当に醜いですし、テストの複雑さに見合う価値があるかどうかはわかりませんが、RouteConfig.Register コードを変更せずにそれを行う方法は次のとおりです。

[TestClass]
public class MyTestClass
{
    [TestMethod]
    public void MyTestMethod()
    {
        // Move all files needed for this test into a subdirectory named bin.
        Directory.CreateDirectory("bin");

        foreach (var file in Directory.EnumerateFiles("."))
        {
            File.Copy(file, "bin\\" + file, overwrite: true);
        }

        // Create a new ASP.NET host for this directory (with all the binaries under the bin subdirectory); get a Remoting proxy to that app domain.
        RouteProxy proxy = (RouteProxy)ApplicationHost.CreateApplicationHost(typeof(RouteProxy), "/", Environment.CurrentDirectory);

        // Call into the other app domain to run route registration and get back the route count.
        int count = proxy.RegisterRoutesAndGetCount();

        Assert.IsTrue(count > 0);
    }

    private class RouteProxy : MarshalByRefObject
    {
        public int RegisterRoutesAndGetCount()
        {
            RouteCollection routes = new RouteCollection();

            RouteConfig.RegisterRoutes(routes); // or just call routes.MapMvcAttributeRoutes() if that's what you want, though I'm not sure why you'd re-test the framework code.

            return routes.Count;
        }
    }
}

属性ルートのマッピングでは、属性を取得するために使用しているすべてのコントローラーを見つける必要があります。これには、明らかに ASP.NET 用に作成されたアプリ ドメインでのみ機能するビルド マネージャーにアクセスする必要があります。

于 2013-11-20T21:51:57.423 に答える
-5

ここで何をテストしていますか?サードパーティの拡張メソッドをテストしているようです。単体テストを使用してサード パーティのコードをテストするべきではありません。

于 2013-11-18T01:24:00.450 に答える