ASP.NET Mvc - System.Web.Compilation.CompilationLock ASP.NET Mvc中的System.Web.Compilation.CompilationLock是什么?

7
我将尝试使用NUnit对一些代码进行单元测试。我有一个方法:
    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);
    }

我的默认控制器工厂是MvcContrib的StructureMap控制器工厂。我还使用MvcContrib的MvcMockHelpers来帮助我模拟HttpContextBase。

我尝试测试的控制器调用了上述RenderRoute方法,并在以下位置出现错误:

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

出现错误:

Controllers.WidgetControllerTests.CanCreateWidgetOnPage: System.Web.HttpException: “System.Web.Compilation.CompilationLock”的类型初始化程序引发了异常。 ----> System.TypeInitializationException: “System.Web.Compilation.CompilationLock”的类型初始化程序引发了异常。 ----> System.NullReferenceException:未将对象引用设置到对象的实例上。

我对单元测试/模拟测试还比较新,很可能我没有看到一些简单的东西。

这是我当前运行的测试:

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

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

希望您能提供帮助。

我已将问题简化为以下简单的单元测试:

    [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);
    }
1个回答

3

当我尝试对自己编写的控制器工厂进行单元测试时,遇到了同样的问题。

问题似乎来自ControllerTypeCache在第一次调用时尝试迭代所有相关程序集并使用BuildManager进行此操作。DefaultControllerFactory通过使用BuildManager属性与实例交互而不是直接耦合来实现这一点,但不幸的是该属性标记为internal。 MVC框架单元测试能够访问MVC程序集的内部,而我们其他人则不能。

在查看MVCContrib如何单元测试其控制器工厂后,我发现它们使用一个扩展方法助手,通过反射访问私有属性覆盖控制器缓存。

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);
    }
}

将这段代码添加到我的单元测试项目后,我就能够使用 controllerFactory.InitializeWithControllerTypes(new[] {typeof(MockController)}); 将自己的 MockController 类型添加到控制器类型缓存中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接