2009-04-07 33 views
7

Tôi đang cố gắng kiểm tra đơn vị một số mã sử dụng NUnit. Tôi có một phương pháp:ASP.NET Mvc - System.Web.Compilation.CompilationLock

public static string RenderRoute(HttpContextBase context, RouteValueDictionary values) 
    { 
     var routeData = new RouteData(); 
     foreach (var kvp in values) 
     { 
      routeData.Values.Add(kvp.Key, kvp.Value); 
     } 

     string controllerName = routeData.GetRequiredString("controller"); 
     var requestContext = new RequestContext(context, routeData); 
     IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory(); 
     IController controller = factory.CreateController(requestContext, controllerName); 

     var ActionInvoker = new ControllerActionInvoker(); 
     var controllerContext = new ControllerContext(requestContext, (ControllerBase)controller); 
     ((ControllerBase)controller).ControllerContext = controllerContext; 

     string actionName = routeData.GetRequiredString("action"); 

     Action action = delegate { ActionInvoker.InvokeAction(controllerContext, actionName); }; 

     return new BlockRenderer(context).Capture(action); 
    } 

Nhà điều khiển mặc định của tôi là nhà máy điều khiển cấu trúc MapMap từ MvcContrib. Tôi cũng đang sử dụng MvcMockHelpers từ MvcContrib để giúp tôi giả lập HttpContextBase.

Bộ điều khiển Tôi đang cố gắng thử nghiệm gọi phương thức RenderRoute trên và thổi lên tại địa chỉ:

IController controller = factory.CreateController(requestContext, controllerName); 

Với các lỗi:

Controllers.WidgetControllerTests.CanCreateWidgetOnPage: System.Web.HttpException: Các loại initializer cho 'System.Web.Compilation.CompilationLock' ném một ngoại lệ. ----> System.TypeInitializationException: Trình khởi tạo kiểu cho 'System.Web.Compilation.CompilationLock' đã ném một ngoại lệ. ----> System.NullReferenceException: Tham chiếu đối tượng không được đặt thành phiên bản của đối tượng.

Tôi khá mới để thử nghiệm đơn vị/chế nhạo và đó là một khả năng tôi không nhìn thấy một cái gì đó đơn giản.

Đây là thử nghiệm Tôi hiện đang chạy:

[Test] 
    public void Test() 
    { 
     HttpContextBase context = MvcMockHelpers.DynamicHttpContextBase(); 
     string s = RenderExtensions.RenderAction<HomeController>(context, a => a.About()); 

     Console.WriteLine(s); 
     Assert.IsNotNullOrEmpty(s); 
    } 

Bất kỳ trợ giúp sẽ được đánh giá cao.

tôi đã đơn giản hóa vấn đề xuống để kiểm tra đơn vị đơn giản này:

[Test] 
    public void Test2() 
    { 
     HttpContextBase context = MvcMockHelpers.DynamicHttpContextBase(); 
     var routeData = new RouteData(); 
     routeData.Values.Add("Controller", "Home"); 
     routeData.Values.Add("Action", "About"); 


     string controllerName = routeData.GetRequiredString("controller"); 
     var requestContext = new RequestContext(context, routeData); 
     IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory(); 
     IController controller = factory.CreateController(requestContext, controllerName); 

     Assert.IsNotNull(controller); 
    } 

Trả lời

3

Tôi chạy vào vấn đề này tương tự khi cố gắng kiểm tra đơn vị một nhà máy điều khiển tôi đã viết.

Sự cố có vẻ xuất phát từ ControllerTypeCache cố gắng lặp qua tất cả các hội đồng được liên kết trên lần gọi đầu tiên và sử dụng BuildManager khi thực hiện việc này. DefaultControllerFactory có vẻ khá mở rộng trong điều này bằng cách sử dụng một thuộc tính BuildManager để tương tác với một cá thể thay vì trực tiếp được ghép đôi nhưng không may là thuộc tính được đánh dấu nội bộ. Các bài kiểm tra đơn vị khuôn khổ MVC có thể truy cập vào nội bộ của hội đồng MVC không giống như phần còn lại của chúng tôi.

Sau khi xem xét đơn vị MVCContrib kiểm tra các nhà máy điều khiển của họ như thế nào, tôi thấy họ đang sử dụng trình trợ giúp phương thức mở rộng ghi đè bộ đệm điều khiển bằng cách sử dụng sự phản chiếu để truy cập thuộc tính riêng.

using System; 
using System.Linq; 
using System.Reflection; 
using System.Web.Mvc; 

public static class ControllerFactoryTestExtension 
{ 
    private static readonly PropertyInfo _typeCacheProperty; 
    private static readonly FieldInfo _cacheField; 

    static ControllerFactoryTestExtension() 
    { 
     _typeCacheProperty = typeof(DefaultControllerFactory).GetProperty("ControllerTypeCache", BindingFlags.Instance | BindingFlags.NonPublic); 
     _cacheField = _typeCacheProperty.PropertyType.GetField("_cache", BindingFlags.NonPublic | BindingFlags.Instance); 
    } 

    /// <summary> 
    /// Replaces the cache field of a the DefaultControllerFactory's ControllerTypeCache. 
    /// This ensures that only the specified controller types will be searched when instantiating a controller. 
    /// As the ControllerTypeCache is internal, this uses some reflection hackery. 
    /// </summary> 
    public static void InitializeWithControllerTypes(this IControllerFactory factory, params Type[] controllerTypes) 
    { 
     var cache = controllerTypes 
      .GroupBy(t => t.Name.Substring(0, t.Name.Length - "Controller".Length), StringComparer.OrdinalIgnoreCase) 
      .ToDictionary(g => g.Key, g => g.ToLookup(t => t.Namespace ?? string.Empty, StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); 

     var buildManager = _typeCacheProperty.GetValue(factory, null); 
     _cacheField.SetValue(buildManager, cache); 
    } 
} 

Sau khi thêm rằng đối với dự án thử nghiệm đơn vị của tôi, tôi đã có thể thêm loại MockController của riêng tôi để bộ nhớ cache loại điều khiển sử dụng controllerFactory.InitializeWithControllerTypes(new[] {typeof(MockController)});