2010-08-13 35 views
5

我嘗試使用MVC 3 Preview 1的新Razor視圖引擎,並且真的很想使用NUnit/Moq編寫簡單的單元測試。我還沒有看到這個實際做法的例子 - 儘管它是Razor真正的銷售功能之一。因此,如果我有一個Controller,它使用一個DBConext對象(EF4 CTP代碼優先),並且該視圖呈現一個下拉列表,該列表基於在控制器上調用的操作中加載的模型中提供的項目列表,我希望能夠測試該元素中已有項目。針對基於Razor的視圖編寫單元測試的指針所需視圖

這裏是我的控制器:

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

} 

這裏是我的模型:

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

這是我的觀點:

@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> 
} 

測試代碼我到目前爲止如下:

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

運行測試時,我只是得到一個NullReferenceException。

任何想法/指針等將受到歡迎。我非常希望得到這個工作,所以我可以在未來對我的觀點做TDD。

在此先感謝!

+0

對不起 - 只是完成了我的測試代碼更新 - 仍然抓我的頭雖然:( – dextermixwith 2010-08-13 15:01:02

+0

運氣好嗎? 我發現了一種方法,使這個功能,請分享... thx – 2010-12-13 10:32:18

回答

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並且是public(IIRC),因此您應該能夠查找System.Web.Mvc.dll中該文章/示例中引用的所有類。

編譯頁面後,只需加載生成的類,傳入模擬的上下文對象,然後調用Execute()。既然你已經有了一個用於頁面的CodeDOM樹(當你使用Razor引擎時),你甚至可以調整基類,而不是System.Web.Mvc.WebViewPage,它繼承自一個Test頁面基類,它可以讓你交換替換上下文對象等。

+4

謝謝你 - 它doesn聽起來並不像我所認爲的那樣簡單:我會盡力去做的 像我這樣的Soudns可能需要耐心等待Razor的新版本 – dextermixwith 2010-08-16 13:04:37

+0

我同意這不是很直接然而,我認爲Razor極大地提高了人們編寫能夠實際運行Razor查看頁面的測試工具的能力,而無需加載ASP。Net(儘管你必須模擬框架的元素,比如HTTP Context和其他ASP.Net)。 – 2010-08-16 15:36:03

+0

沒想到需要在內存中編譯代碼來啓動剃刀,但它的工作地獄。 – mfloryan 2011-06-25 21:38:52

0

我已經寫過關於單元測試剃刀視圖。這是醜陋的和各種各樣的討厭,但它的工作原理,我能夠適應它來測試我所有項目的所有意見。

http://httputility.com/articles/unit-testing-razor-views.html

雖然長期居住項目,我會建議等待由MVC團隊做了一個測試工具。