需要为基于Razor的View编写单元测试的指针

dex*_*ith 5 asp.net-mvc nunit unit-testing razor asp.net-mvc-2

我正在尝试使用MVC 3 Preview 1的新Razor视图引擎,并且真的想用NUnit/Moq编写一个简单的单元测试.我还没有看到任何实际的例子 - 尽管它是Razor真正的销售功能之一.

因此,如果我有一个使用DBConext对象的Controller(首先是EF4 CTP代码),并且视图根据控制器上调用的动作中加载的模型中提供的项目列表呈现下拉列表,我会喜欢能够测试该元素是否填充了其中的项目.

这是我的控制器:

public class WeatherReportController : Controller, IWeatherReportController
{
    private IWeatherDb _weatherDb;

    public WeatherReportController()
    {
        this._weatherDb = new WeatherDb();
    }

    public ActionResult Index()
    {
        WeatherReportIndexModel model = new WeatherReportIndexModel
        {
            Report = new WeatherReport {
                Username = this.HttpContext.User.Identity.Name,
                WeatherType = new WeatherType()
            },
            WeatherTypeList = _weatherDb.GetAllWeatherTypes()
        };
        return View(model);
    }

}
Run Code Online (Sandbox Code Playgroud)

这是我的模型:

public class WeatherReportIndexModel
{
    private IList<WeatherType> _weatherTypeList = new List<WeatherType>();
    public IList<WeatherType> WeatherTypeList { 
        get 
        {
            return _weatherTypeList;
        }
        set 
        {
            _weatherTypeList = value;
        }
    }

    [DisplayName("Type of Weather")]
    public IList<SelectListItem> WeatherTypeSelectItemList
    {
        get
        {
            int id = this.Report.WeatherType == null ? 0 : this.Report.WeatherType.WeatherTypeId;
            List<SelectListItem> selectListItems = this.WeatherTypeList.Select(weatherType => new SelectListItem
                                                                                   {
                                                                                       Value = weatherType.WeatherTypeId.ToString(),
                                                                                       Text = weatherType.Name,
                                                                                       Selected = weatherType.WeatherTypeId == id
                                                                                   }).ToList();
            selectListItems.Insert(0, new SelectListItem { Selected = (this.Report.WeatherType == null), Text = "Select Type of Weather", Value = "0" });
            return selectListItems;
        }
    }

    public WeatherReport Report { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这是我的观点:

@inherits System.Web.Mvc.WebViewPage<Web.UI.Models.WeatherReportIndexModel>

@{
    View.Title = "Index";
    LayoutPage = "~/Views/Shared/_Layout.cshtml";
}

<h2>Index</h2>


@using (Html.BeginForm()) {
    <div>
        <fieldset>
            <legend>New Weather Report</legend>
            <div class="editor-label">
                @Html.LabelFor(m => m.Report.WeatherType.WeatherTypeId)
                @Html.DropDownListFor(m => m.Report.WeatherType.WeatherTypeId, Model.WeatherTypeSelectItemList)
    <input type="submit" value="Log On" />
            </div>
  </fieldset>
 </div>
 }
Run Code Online (Sandbox Code Playgroud)

我到目前为止的测试代码如下:

[TestFixture]
public class WeatherReportViewTests
{
    [Test]
    public void Can_render_weather_report_index_view_correctly()
    {

        var mockControllerContext = new Mock<ControllerContext>();
        var mockSession = new Mock<HttpSessionStateBase>();

        mockControllerContext.Setup(p => p.HttpContext.Request.HttpMethod).Returns("POST");
        mockControllerContext.Setup(p => p.HttpContext.Request.UserHostAddress).Returns("1.1.1.1");
        mockControllerContext.Setup(p => p.HttpContext.Session).Returns(mockSession.Object);
        mockControllerContext.Setup(p => p.HttpContext.Request.LogonUserIdentity).Returns(WindowsIdentity.GetCurrent());

        var routeData = new RouteData();
        routeData.Values.Add("controller", "WeatherReport");
        routeData.Values.Add("action", "Index");

        var viewEngine = new CshtmlViewEngine();
        var view = viewEngine.FindView(mockControllerContext.Object, "Index", "_Layout", false);
        var viewReponse = view.ToString();

        Assert.That(viewReponse, Contains.Substring("Sunny Intervals"));
    }
}
Run Code Online (Sandbox Code Playgroud)

运行测试时,我只是得到一个NullReferenceException.

任何想法/指针等都是受欢迎的.我真的很想让这个工作,所以我将来可以对我的观点做TDD.

提前致谢!

And*_*rse 2

我建议完全避免使用 CshtmlViewEngine 类并自行启动 Razor 引擎。我在这里写了一篇关于在 ASPX 之外编译 Razor 视图的博客文章:http://vibrantcode.com/blog/2010/7/22/using-the-razor-parser-outside-of-aspnet.html

在 MVC3 的预览版 1 中,Razor 引擎嵌入在 System.Web.Mvc 中并且是公共的 (IIRC),因此您应该能够在 System.Web.Mvc.dll 中找到该帖子/示例中引用的所有类。

编译页面后,只需加载生成的类,传入模拟的上下文对象,然后调用 Execute()。由于您已经获得了页面的 CodeDOM 树(当您使用 Razor 引擎时),您甚至可以调整基类,以便它继承自测试页面基类,而不是 System.Web.Mvc.WebViewPage,这样您就可以交换替换上下文对象等。

  • 谢谢你——这听起来并不像我想象的那么简单:(不过我会尝试一下。听起来我可能需要耐心等待 Razor 的新版本。 (4认同)