编程语言
首页 > 编程语言> > c#-如何在m​​vc3中使用razor语法测试视图?

c#-如何在m​​vc3中使用razor语法测试视图?

作者:互联网

我正在编写代码以测试C#MVC3应用程序.我可以测试控制器,但是如何测试视图中的代码?这包括javascript和剃刀样式的代码.

有没有可用的工具可以模拟视图或测试视图以及C#中的javascript?

解决方法:

以下是关于测试视图的渲染输出的信息.例如,可以将文本输出加载到DOM中,以使用XPath进行进一步分析(对于XHTML使用XmlReader或对于SGML样式HTML使用HtmlAgilityPack).使用一些不错的帮助程序方法,可以轻松检查视图的特定部分,例如测试// a [@href =’#’]或要测试的其他任何东西.这有助于使单元测试更加稳定.

有人希望使用Razor而不是“爆炸式” WebForms引擎时,这很容易,但是事实却恰恰相反,这是由于Razor视图引擎的许多内部工作原理以及视图使用的部分(尤其是HtmlHelper) HTTP请求生命周期.实际上,正确测试生成的输出需要大量运行代码才能获得可靠且适当的结果,甚至在混合环境中使用诸如可移植区域(来自MVCContrib项目)之类的奇特的东西时,则更是如此.

用于操作和URL的HTML帮助器要求正确初始化路由,正确设置路由字典,控制器也必须存在,以及与加载视图数据有关的其他“陷阱”,例如设置视图数据字典…

我们最终创建了一个ViewRenderer类,该类实际上将在要测试的Web物理路径上实例化一个应用程序主机(可以静态缓存,由于初始化滞后,无法对每个单个测试进行重新初始化):

host = (ApplicationHost)System.Web.Hosting.ApplicationHost.CreateApplicationHost(typeof(ApplicationHost), "/", physicalDir.FullName);

由于主机将被加载到单独的应用程序域中,因此ApplicationHost类又继承自MarshalByRefObject.主机执行各种有害的初始化工作,以正确初始化HttpApplication(global.asax.cs中的代码,用于注册路由等),同时禁用某些方面(如身份验证和授权).被警告,严重的黑客攻击.使用风险自负.

public ApplicationHost() {
    ApplicationMode.UnitTesting = true; // set a flag on a global helper class to indicate what mode we're running in; this flag can be evaluated in the global.asax.cs code to skip code which shall not run when unit testing
    // first we need to tweak the configuration to successfully perform requests and initialization later
    AuthenticationSection authenticationSection = (AuthenticationSection)WebConfigurationManager.GetSection("system.web/authentication");
    ClearReadOnly(authenticationSection);
    authenticationSection.Mode = AuthenticationMode.None;
    AuthorizationSection authorizationSection = (AuthorizationSection)WebConfigurationManager.GetSection("system.web/authorization");
    ClearReadOnly(authorizationSection);
    AuthorizationRuleCollection authorizationRules = authorizationSection.Rules;
    ClearReadOnly(authorizationRules);
    authorizationRules.Clear();
    AuthorizationRule rule = new AuthorizationRule(AuthorizationRuleAction.Allow);
    rule.Users.Add("*");
    authorizationRules.Add(rule);
    // now we execute a bogus request to fully initialize the application
    ApplicationCatcher catcher = new ApplicationCatcher();
    HttpRuntime.ProcessRequest(new SimpleWorkerRequest("/404.axd", "", catcher));
    if (catcher.ApplicationInstance == null) {
        throw new InvalidOperationException("Initialization failed, could not get application type");
    }
    applicationType = catcher.ApplicationInstance.GetType().BaseType;
}

ClearReadOnly方法使用反射使内存中的Web配置可变:

private static void ClearReadOnly(ConfigurationElement element) {
    for (Type type = element.GetType(); type != null; type = type.BaseType) {
        foreach (FieldInfo field in type.GetFields(BindingFlags.Instance|BindingFlags.NonPublic|BindingFlags.DeclaredOnly).Where(f => typeof(bool).IsAssignableFrom(f.FieldType) && f.Name.EndsWith("ReadOnly", StringComparison.OrdinalIgnoreCase))) {
            field.SetValue(element, false);
        }
    }
}

ApplicationCatcher是一个“空” TextWriter,用于存储应用程序实例.我找不到另一种方法来初始化应用程序实例并获取它.它的核心非常简单.

public override void Close() {
    Flush();
}

public override void Flush() {
    if ((applicationInstance == null) && (HttpContext.Current != null)) {
        applicationInstance = HttpContext.Current.ApplicationInstance;
    }
}

现在,这使我们能够渲染几乎任何(Razor)视图,就像将其托管在真实的Web服务器中一样,从而创建几乎完整的HTTP生命周期来渲染它:

private static readonly Regex rxControllerParser = new Regex(@"^(?<areans>.+?)\.Controllers\.(?<controller>[^\.]+)Controller$", RegexOptions.CultureInvariant|RegexOptions.IgnorePatternWhitespace|RegexOptions.ExplicitCapture);

public string RenderViewToString<TController, TModel>(string viewName, bool partial, Dictionary<string, object> viewData, TModel model) where TController: ControllerBase {
    if (viewName == null) {
        throw new ArgumentNullException("viewName");
    }
    using (StringWriter sw = new StringWriter()) {
        SimpleWorkerRequest workerRequest = new SimpleWorkerRequest("/", "", sw);
        HttpContextBase httpContext = new HttpContextWrapper(HttpContext.Current = new HttpContext(workerRequest));
        RouteData routeData = new RouteData();
        Match match = rxControllerParser.Match(typeof(TController).FullName);
        if (!match.Success) {
            throw new InvalidOperationException(string.Format("The controller {0} doesn't follow the common name pattern", typeof(TController).FullName));
        }
        string areaName;
        if (TryResolveAreaNameByNamespace<TController>(match.Groups["areans"].Value, out areaName)) {
            routeData.DataTokens.Add("area", areaName);
        }
        routeData.Values.Add("controller", match.Groups["controller"].Value);
        ControllerContext controllerContext = new ControllerContext(httpContext, routeData, (ControllerBase)FormatterServices.GetUninitializedObject(typeof(TController)));
        ViewEngineResult engineResult = partial ? ViewEngines.Engines.FindPartialView(controllerContext, viewName) : ViewEngines.Engines.FindView(controllerContext, viewName, null);
        if (engineResult.View == null) {
            throw new FileNotFoundException(string.Format("The view '{0}' was not found", viewName));
        }
        ViewDataDictionary<TModel> viewDataDictionary = new ViewDataDictionary<TModel>(model);
        if (viewData != null) {
            foreach (KeyValuePair<string, object> pair in viewData) {
                viewDataDictionary.Add(pair.Key, pair.Value);
            }
        }
        ViewContext viewContext = new ViewContext(controllerContext, engineResult.View, viewDataDictionary, new TempDataDictionary(), sw);
        engineResult.View.Render(viewContext, sw);
        return sw.ToString();
    }
}

也许这可以帮助您获得一些结果.总的来说,许多人认为麻烦的测试视图不值得付出努力.我会让你当法官.

标签:unit-testing,razor,c,asp-net-mvc-3
来源: https://codeday.me/bug/20191201/2084411.html