我正在尝试使用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);
}
Run Code Online (Sandbox Code Playgroud)
我的默认controllerfactory是MvcContrib的StructureMap控制器工厂.我也使用MvcContrib的MvcMockHelpers来帮助我模拟HttpContextBase.
我试图测试的控制器调用上面的RenderRoute方法并在以下情况下爆炸:
IController controller = factory.CreateController(requestContext, controllerName);
Run Code Online (Sandbox Code Playgroud)
有错误:
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);
}
Run Code Online (Sandbox Code Playgroud)
任何帮助,将不胜感激.
我已将问题简化为这个简单的单元测试:
[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);
}
Run Code Online (Sandbox Code Playgroud)
当我尝试对我编写的控制器工厂进行单元测试时,我遇到了同样的问题。
该问题似乎来自于 ControllerTypeCache 在第一次调用时尝试迭代所有关联的程序集,并使用 BuildManager 来执行此操作。通过使用 BuildManager 属性与实例交互而不是直接耦合,DefaultControllerFactory 看起来在这方面具有相当的可扩展性,但不幸的是该属性被标记为内部。与我们其他人不同,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);
}
}
Run Code Online (Sandbox Code Playgroud)
将其添加到我的单元测试项目后,我可以使用以下命令将我自己的 MockController 类型添加到控制器类型缓存中controllerFactory.InitializeWithControllerTypes(new[] {typeof(MockController)});
| 归档时间: |
|
| 查看次数: |
3128 次 |
| 最近记录: |