2010-04-26 29 views
20

Tôi đơn vị thử nghiệm các tuyến đường của tôi trong ASP.NET MVC 2. Tôi đang sử dụng MSTest và tôi đang sử dụng các khu vực là tốt.Đơn vị thử nghiệm ASP.NET MVC 2 tuyến đường với các khu vực bails ra trên AreaRegistration.RegisterAllAreas()

[TestClass] 
public class RouteRegistrarTests 
{ 
    [ClassInitialize] 
    public static void ClassInitialize(TestContext testContext) 
    { 
     RouteTable.Routes.Clear(); 

     RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 
     RouteTable.Routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" }); 

     AreaRegistration.RegisterAllAreas(); 

     routes.MapRoute(
      "default", 
      "{controller}/{action}/{id}", 
      new { controller = "Home", action = "Index", id = UrlParameter.Optional } 
     ); 
    } 

    [TestMethod] 
    public void RouteMaps_VerifyMappings_Match() 
    { 
     "~/".Route().ShouldMapTo<HomeController>(n => n.Index()); 
    } 
} 

Khi nó thực hiện AreaRegistration.RegisterAllAreas() tuy nhiên, nó ném ngoại lệ này:

System.InvalidOperationException: System.InvalidOperationException: Phương pháp này không thể được gọi trong pre-start khởi sân khấu của ứng dụng.

Vì vậy, tôi nghĩ rằng tôi không thể gọi nó từ bộ khởi tạo lớp của mình. Nhưng khi có thể tôi gọi nó? Tôi rõ ràng không có Application_Start trong thử nghiệm của tôi.

+0

Mục đích của tham số 'testContext' là gì? Nó không bao giờ được sử dụng. – MEMark

+0

@MEMark - bắt buộc phải có [ClassInitialize] – Swati

+0

@Swati Tất nhiên. Tôi đã không sử dụng MSTest trong một thời gian dài. – MEMark

Trả lời

29

tôi giải quyết này bằng cách tạo một thể hiện của lớp AreaRegistration của tôi và gọi phương thức RegisterArea.

Ví dụ, với một vùng có tên là "Catalog" với tuyến đường này:

public override void RegisterArea(AreaRegistrationContext context) 
{ 
    context.MapRoute(
     "Catalog_default", 
     "Catalog/{controller}/{action}/{id}", 
     new {controller = "List", action = "Index", id = "" } 
); 
} 

Đây là phương pháp thử nghiệm của tôi:

[TestMethod] 
public void TestCatalogAreaRoute() 
{ 
    var routes = new RouteCollection(); 

    // Get my AreaRegistration class 
    var areaRegistration = new CatalogAreaRegistration(); 
    Assert.AreEqual("Catalog", areaRegistration.AreaName); 

    // Get an AreaRegistrationContext for my class. Give it an empty RouteCollection 
    var areaRegistrationContext = new AreaRegistrationContext(areaRegistration.AreaName, routes); 
    areaRegistration.RegisterArea(areaRegistrationContext); 

    // Mock up an HttpContext object with my test path (using Moq) 
    var context = new Mock<HttpContextBase>(); 
    context.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath).Returns("~/Catalog"); 

    // Get the RouteData based on the HttpContext 
    var routeData = routes.GetRouteData(context.Object); 

    Assert.IsNotNull(routeData, "Should have found the route"); 
    Assert.AreEqual("Catalog", routeData.DataTokens["area"]); 
    Assert.AreEqual("List", routeData.Values["controller"]); 
    Assert.AreEqual("Index", routeData.Values["action"]); 
    Assert.AreEqual("", routeData.Values["id"]); 
} 
+0

Tôi đoán đó là một giải pháp tốt, cảm ơn. –

+0

Tôi không thể làm việc này với khu vực 'Quản trị' của mình. routeData trả về null. MVC 4 có thể đủ khác để gây ra điều này không? Tôi nhận được một chút xa hơn với thử nghiệm ban đầu của tôi là rất giống nhau, nhưng nó không phù hợp trên tuyến đường khu vực và rơi thông qua các tuyến đường mặc định để thay thế. –

+0

Aha! Đã phát hiện ra ... Tôi đã không đặt bộ điều khiển mặc định khi đăng ký khu vực của mình (đó là mặc định của mẫu). Plus 1. Cảm ơn! –

10

Cũng không có chỗ nào trong dự án thử nghiệm, bạn có thể đặt AreaRegistration.RegisterAllAreas(); để làm cho nó hoạt động, bởi vì nó sử dụng lớp System.Web.Compilation.BuildManager để biên dịch mã cho trang web và không thành công nếu nó được gọi bên ngoài đường dẫn ASP.NET. Tôi nghĩ rằng đó là loại lỗi, bởi vì nó thực sự làm cho các bài kiểm tra rất khó để chạy.

Nhưng tôi đã phát minh ra một cách giải quyết 2 bước :)

Trước tiên, bạn nên thay đổi tập tin App.Config của dự án thử nghiệm của bạn

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
    <appSettings> 

    </appSettings> 

    <connectionStrings> 

    </connectionStrings> 
    <system.web> 
     <compilation debug="true"> 
      <assemblies> 
       <add assembly="!!!NAME_OF_YOUR_MVC_WEB_ASSEMBLY!!!"/>  
      </assemblies> 
     </compilation> 
    </system.web> 
    </configuration> 

Actualy bạn nên refference tất cả các cụm có chứa xuống của AreaRegistration. Thứ hai thêm mã xấu này trước khi AreaRegistration.RegisterAllAreas();

typeof(BuildManager).GetProperty("PreStartInitStage", BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, 2, null); 

typeof(BuildManager).GetField("_topLevelFilesCompiledStarted", BindingFlags.NonPublic | BindingFlags.Instance).SetValue( typeof(BuildManager).GetField("_theBuildManager", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null), true); 

này chỉ hoạt động đối với Net 4.0 trở lên

+0

Cảm ơn. Mặc dù nó không phải là rất đẹp, nó làm cho vượt qua thử nghiệm của tôi. Tôi thấy nó thất bại khi gỡ lỗi thử nghiệm mặc dù, ném một ngoại lệ khác (một cái gì đó về một tên tập tin có chứa một nhân vật bất hợp pháp). Dù sao, tôi sẽ chấp nhận câu trả lời của bạn ngay bây giờ. Tôi vẫn hy vọng cho một giải pháp thích hợp. –

+0

Woks tốt! Thật là một hack xấu xí, mặc dù ... –

+0

Lifesaver, chúc mừng! –

1

Tôi nghĩ rằng bạn đang tìm kiếm lớp TestHelper trong thư viện MVC contrib. Hãy xem xét các bài kiểm tra trong MVC Contrib (nó được ẩn đi trong đó). Bạn sẽ thấy rằng tất cả mọi thứ được độc đáo chế giễu.

MVCContrib.UnitTests\TestHelper\RoutesTest.cs - phải cập nhật wiki! Chúc may mắn

using System.Web.Mvc; 
using System.Web.Routing; 
using NUnit.Framework; 

namespace MVCContrib.Application.UnitTests.TestHelper 
{ 
    /// <summary> 
    /// Summary description for UserRoutesTest 
    /// </summary> 
    [TestFixture] 
    public class UserRoutesTest 
    { 
     [TestFixtureSetUp] 
     public void Setup() 
     { 
      var routes = RouteTable.Routes; 
      routes.Clear(); 
      routes.MapRoute(
       "Default",            // Route name 
       "{controller}",           // URL with parameters 
       new { controller = "Home", action = "Index", id = "" } // Parameter defaults 
       ); 

     } 

     [Test] 
     public void homeIndex() 
     { 
      "~/user" 
       .ShouldMapTo<HomeController>(action => action.Index()); 
     } 


     [Test] 
     public void HomeShow() 
     { 
         "~/home" 
          .GivenIncomingAs(HttpVerbs.Put) 
          .ShouldMapTo<HomeController>(action => action.Index()); 
     } 

    } 
} 
+1

Như bạn có thể thấy trong ví dụ mã của tôi, tôi đã sử dụng trình trợ giúp kiểm tra MvcContrib (gọi phương thức mở rộng 'ShouldMapTo'). Dường như không có sự hỗ trợ cụ thể cho các khu vực trong MvcContrib tại thời điểm này hoặc họ đã không làm cho nó hiển nhiên. Ngay cả khi có đủ hỗ trợ cho các vùng thử nghiệm, tôi vẫn gặp vấn đề là mã đăng ký tuyến đường của tôi ném ngoại lệ này khi được gọi từ một thử nghiệm đơn vị do lệnh gọi 'AreaRegistration.RegisterAllAreas()'. –

11

tôi biết tôi chiming vào cuối năm ở đây, nhưng tôi chỉ làm việc thông qua vấn đề này bản thân mình. Tương tự như giải pháp như Jason (đăng ký một khu vực tại một thời điểm), nhưng như bạn đang sử dụng MvcContrib.TestHelper thay vì làm mocking của riêng tôi.

[TestInitialize] 
public void Setup() { 
    RouteTable.Routes.Clear(); 
    var areaReg = new AdminAreaRegistration(); 
    areaReg.RegisterArea(new AreaRegistrationContext(areaReg.AreaName, RouteTable.Routes)); 
} 

[TestMethod] 
public void admin_should_map_to_home() { 
    "~/Admin".ShouldMapTo<HomeController>(c => c.Index()); 
} 

Lưu ý rằng MvcContrib có sự phụ thuộc khó vào Rhino Mocks. Trong khi tôi thích sử dụng Moq, tôi tốt với việc bao gồm các dll Rhino chỉ để đạt được chức năng này tốt đẹp.

+0

+1 Không bao giờ quá muộn để có câu trả lời hay – StarTrekRedneck

+1

Thật không may, MvcContrib không có hỗ trợ MVC 4 hoặc MVC 5, trừ khi bạn tự biên dịch nó. Tôi nhận được một lỗi vì loại bộ điều khiển không phù hợp với những gì nó đã được biên dịch với trong ShouldMapTo (). Cảm ơn, nhưng bây giờ điều này là quá nhiều rắc rối. – NightOwl888

2

Đây là phiên bản đẹp với các phương pháp kết hợp.

Mã sử ​​dụng từ:


[TestClass] 
public class RoutesTest : RoutesTestClassBase<SomeAreaRegistration> 
{ 
    [TestMethod] 
    public void IdWithoutName() 
    { 
     // Area-Name is retrieved from the Registration 
     // and prepended as "~/AreaName/" 

     TestRoute("Contacts/Show/0627ED05-BF19-4090-91FC-AD3865B40983", new { 
      controller = "Contacts", 
      action = "Show", 
      id = "0627ED05-BF19-4090-91FC-AD3865B40983" 
     }); 
    } 

    [TestMethod] 
    public void IdAndName() 
    { 
     TestRoute("Contacts/Show/0627ED05-BF19-4090-91FC-AD3865B40983-Some-name", new 
     { 
      controller = "Contacts", 
      action = "Show", 
      id = "0627ED05-BF19-4090-91FC-AD3865B40983", 
      name= "Some-name" 
     }); 
    } 
} 

Các cơ sở-cố:

public class RoutesTestClassBase<TAreaRegistration> 
{ 
    protected void TestRoute(string url, object expectations) 
    { 
     var routes = new RouteCollection(); 
     var areaRegistration = (AreaRegistration)Activator.CreateInstance(typeof(TAreaRegistration)); 

     // Get an AreaRegistrationContext for my class. Give it an empty RouteCollection 
     var areaRegistrationContext = new AreaRegistrationContext(areaRegistration.AreaName, routes); 
     areaRegistration.RegisterArea(areaRegistrationContext); 

     url = "~/" + areaRegistration.AreaName + "/" + url; 

     // Mock up an HttpContext object with my test path (using Moq) 
     var context = new Mock<HttpContextBase>(); 
     context.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath).Returns(url); 

     // Get the RouteData based on the HttpContext 
     var routeData = routes.GetRouteData(context.Object); 

     Assert.IsNotNull(routeData, "Should have found the route"); 
     Assert.AreEqual(areaRegistration.AreaName, routeData.DataTokens["area"]); 

     foreach (PropertyValue property in GetProperties(expectations)) 
     { 
      Assert.IsTrue(string.Equals(property.Value.ToString(), 
       routeData.Values[property.Name].ToString(), 
       StringComparison.OrdinalIgnoreCase) 
       , string.Format("Expected '{0}', not '{1}' for '{2}'.", 
       property.Value, routeData.Values[property.Name], property.Name)); 
     } 
    } 

    private static IEnumerable<PropertyValue> GetProperties(object o) 
    { 
     if (o != null) 
     { 
      PropertyDescriptorCollection props = TypeDescriptor.GetProperties(o); 
      foreach (PropertyDescriptor prop in props) 
      { 
       object val = prop.GetValue(o); 
       if (val != null) 
       { 
        yield return new PropertyValue { Name = prop.Name, Value = val }; 
       } 
      } 
     } 
    } 

    private sealed class PropertyValue 
    { 
     public string Name { get; set; } 
     public object Value { get; set; } 
    } 
} 
3

Để Make AreaRegistration.RegisterAllAreas() ngày làm việc, chạy đoạn mã sau đầu tiên:

Xin lưu ý typeof(YourMvcSiteApplication).Assembly nên dẫn đến lắp ráp web MVC của bạn !!!

object manager = typeof(BuildManager).GetField("_theBuildManager", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null); 
    manager.SetField("_skipTopLevelCompilationExceptions", true); 
    manager.SetField("_topLevelFilesCompiledStarted", true); 
    manager.SetField("_topLevelReferencedAssemblies", new List<Assembly> { typeof(YourMvcSiteApplication).Assembly }); 

Đây là phần mở rộng phương pháp SetField() của một đối tượng dụ:

public static void SetField<T>(this object source, string fieldName, T value) 
    { 
     var type = source.GetType(); 
     var info = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 
     if (info != null) 
     { 
      info.SetValue(source, value); 
     } 
    } 

Các mã trên làm việc cho .NET 3.5, tôi đã không kiểm tra cho .NET 4 hoặc 4.5 chưa!

3

Đây là một vài năm muộn, nhưng tôi đã chia sẻ. Tôi đang đăng ký tất cả các khu vực sử dụng sự phản chiếu.

public void RegisterAllAreas() 
{ 
    List<AreaRegistration> objects = new List<AreaRegistration>(); 

    foreach (Type type in Assembly.GetAssembly(typeof(MvcApplication)).GetTypes() 
      .Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(AreaRegistration)))) 
    { 
     objects.Add((AreaRegistration)Activator.CreateInstance(type)); 
    } 

    objects.ForEach(area => area.RegisterArea(new AreaRegistrationContext(area.AreaName, routes))); 
} 
Các vấn đề liên quan