2015-05-11 30 views
1

好了,所以我想測試控制器結果,以確保它返回正確的價值觀,但我遇到了一些與嘲諷控制器環境問題控制器動作。我想要做的就是測試如果某人在頁面上輸入了一個數字(如果JsonResult爲success = true)。但是,在控制器中,我將序列化部分視圖並返回一些包含與請求編號相關的所有數據的HTML。我很樂於在json結果中設置預期結果,但我甚至無法通過測試推進到目前爲止。序列化部分視圖時,測試會掛起。我真的不關心來測試,但我不知道如何解決其他該函數的結果比創造的controllercontext一些假貨,讓它尋找一個連載的看法。如何測試序列化視圖

這裏是控制器

[HttpPost] 
[RecaptchaControlMvc.CaptchaValidator] 
public JsonResult GetPublicInformation(PublicPortalViewModel model, bool captchaValid) 
{ 
    if (captchaValid == true) 
    { 
     //check to see if their is a request number to look up the request by 
     if (model.RequestNumber != null) 
     { 
      //fill model data using by calling the service 
      model = _service.GetPublicPortalData(model); 
      var content = base.SerializeView("DisplayPublicInformation", model); 
      return Json(new { success = true, htmlContent = content }); 
     } 
     else 
     { 
      return Json(new { success = false, htmlContent = "<span style=\"color: red;\">No request was found with that number, please enter a valid request number.</span>" }); 
     } 
    } 
    else 
    { 
     return Json(new { success = false, htmlContent = "<span style=\"color: red;\">Please enter a valid Captcha Value</span>" }); 
    } 
} 

具體我有這行的問題:

var content = base.SerializeView("DisplayPublicInformation", model); 

這裏是函數的定義:

protected internal virtual string SerializeView(string viewName, object model) 
{ 
    if (string.IsNullOrEmpty(viewName)) 
     viewName = ControllerContext.RouteData.GetRequiredString("action"); 

    this.ViewData.Model = model; 

    using (var sw = new StringWriter()) 
    { 
     // keep getting null reference errors on this line when I write my tests . 
     var viewResult = ViewEngines.Engines.FindPartialView(this.ControllerContext, viewName); 
     var viewContext = new ViewContext(this.ControllerContext, viewResult.View, this.ViewData, this.TempData, sw); 
     // render the view into the stringwriter class 
     viewResult.View.Render(viewContext, sw); 
     // output the rendered string 
     return sw.GetStringBuilder().ToString(); 
    } 
} 

這裏是我的測試我很抱歉如此混亂,但我想發佈我目前的位置。我知道有更多的代碼比需要的。

private PartialViewResult _result; 
private Mock<HttpContextBase> _mockHttpContext; 
private HttpContextBase _httpContext; 
private RouteData _routeData; 
private RouteData _parentRouteData; 

protected Mock<HttpContextBase> HttpContextBaseMock; 
protected Mock<HttpRequestBase> HttpRequestMock; 
protected Mock<HttpResponseBase> HttpResponseMock; 

[Test] 
public void GetPublicInformationValidRequestNumber() 
{ 
    var sut = new PublicPortalController(); 
    SetupRouteData(); 
    HttpContextBaseMock = new Mock<HttpContextBase>(); 
    HttpContextBaseMock.SetupAllProperties(); 
    HttpRequestMock = new Mock<HttpRequestBase>(); 
    HttpResponseMock = new Mock<HttpResponseBase>(); 
    HttpContextBaseMock.SetupGet(x => x.Request).Returns(HttpRequestMock.Object); 
    HttpContextBaseMock.SetupGet(x => x.Response).Returns(HttpResponseMock.Object); 

    var browser = new Mock<HttpBrowserCapabilitiesBase>(MockBehavior.Strict); 
    var request = new Mock<HttpRequestBase>(MockBehavior.Strict); 
    var response = new Mock<HttpResponseBase>(MockBehavior.Strict); 
    var session = new Mock<HttpSessionStateBase>(MockBehavior.Strict); 
    var server = new Mock<HttpServerUtilityBase>(MockBehavior.Strict); 
    var cookies = new HttpCookieCollection(); 
    var items = new ListDictionary(); 

    var mockViewEngine = new Mock<IViewEngine>(); 

    Mock<IView> view = new Mock<IView>(); 
    var viewResult = new ViewEngineResult(new[] { "location1", "location2" }); 

    mockViewEngine 
     .Setup(x => x.FindView(It.IsAny<ControllerContext>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>())) 
     .Returns(viewResult); 

    ViewEngines.Engines.Clear(); 
    ViewEngines.Engines.Add(mockViewEngine.Object); 

    browser.Setup(b => b.IsMobileDevice).Returns(false); 
    request.Setup(r => r.Cookies).Returns(cookies); 
    request.Setup(r => r.ValidateInput()); 
    request.Setup(r => r.UserAgent).Returns("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11"); 
    response.Setup(r => r.Cookies).Returns(cookies); 

    request.Setup(r => r.Browser).Returns(browser.Object); 
    HttpContextBaseMock.Setup(ctx => ctx.Items).Returns(items); 

    var routes = new RouteCollection(); 

    var ControllerContext = new Mock<ControllerContext>(HttpContextBaseMock.Object, _routeData, sut); 

    var controller = new Mock<PublicPortalController>(); 

    ControllerContext.SetupGet(c => c.Controller).Returns(controller.Object); 
    ControllerContext.SetupGet(c => c.HttpContext).Returns(HttpContextBaseMock.Object); 

    sut.Url = new UrlHelper(new RequestContext(HttpContextBaseMock.Object, _routeData), routes); 
    sut.ControllerContext = ControllerContext.Object; 
    var basecontroller = new Mock<RequestITBaseController>(); 

    var fakePublicPortalViewModel = new Mock<PublicPortalViewModel>(); 
    fakePublicPortalViewModel.Setup(m => m.RequestNumber).Returns("23"); 
    bool captchaValid = true; 

    basecontroller.Setup(c => c.SerializeView("DisplayPublicInformation", fakePublicPortalViewModel)).Returns(""); 

    var result = sut.GetPublicInformation(fakePublicPortalViewModel.Object, captchaValid) as JsonResult; 
    dynamic jsonObject = result.Data; 

    Assert.IsTrue(jsonObject.success); 
} 

private void SetupRouteData() 
{ 
    SetupParentRouteData(); 
    var viewContext = new ViewContext { RouteData = _parentRouteData }; 

    _routeData = new RouteData(); 
    _routeData.Values.Add("controller", "PublicPortalController"); 
    _routeData.Values.Add("action", "GetPublicInformation"); 
    _routeData.DataTokens["ParentActionViewContext"] = viewContext; 
} 

private void SetupParentRouteData() 
{ 
    _parentRouteData = new RouteData(); 
    _parentRouteData.Values.Add("controller", "PublicPortalController"); 
    _parentRouteData.Values.Add("action", "Index"); 
} 

回答

2

依賴,得愛的依賴,更好的是靜態依賴。

如果SerializeView底層代碼不是用於單元測試那麼重要,則最容易做的事情是抽象出其方法爲另一種類型,而是注入從基類繼承它的....

事情是這樣的:

public interface IViewHelper 
{ 
    string SerializeView(ControllerContext context, ViewDataDictionary viewData, TempDataDictionary tempData, 
     string viewName, object model); 
}  

public class ViewHelper : IViewHelper 
{ 
    private readonly ViewEngineCollection _viewEngines = ViewEngines.Engines; 

    public string SerializeView(ControllerContext context, ViewDataDictionary viewData, TempDataDictionary tempData, string viewName, object model) 
    { 
     viewData.Model = model; 
     using (var sw = new StringWriter()) 
     { 
      // keep getting null reference errors on this line when I write my tests . 
      var viewResult = _viewEngines.FindPartialView(context, viewName); 
      var viewContext = new ViewContext(context, viewResult.View, viewData, tempData, sw); 
      // render the view into the stringwriter class 
      viewResult.View.Render(viewContext, sw); 
      // output the rendered string 
      return sw.GetStringBuilder().ToString(); 
     } 
    } 
} 

然後在你的控制器:

public PublicPortalController(..., IViewHelper helper) 
{ 
    _helper = helper; 
} 

.... 

public JsonResult GetPublicInformation(PublicPortalViewModel model, bool captchaValid) 
{ 
    .... 
      //fill model data using by calling the service 
      model = _service.GetPublicPortalData(model); 
      var content = _helper.SerializeView(ControllerContext, ViewData, TempData, "DisplayPublicInformation", model); 
      return Json(new { success = true, htmlContent = content }); 
    .... 
} 

現在,在您的測試所有你需要做的是模擬出IViewHelper你應該走笑道:

var mockHelper = new Mock<IViewHelper>(); 
     mockHelper.Setup(
      x => 
       x.SerializeView(It.IsAny<ControllerContext>(), It.IsAny<ViewDataDictionary>(), 
        It.IsAny<TempDataDictionary>(), "", It.IsAny<PublicPortalViewModel>())).Returns("<html></html>"); 

     var sut = new PublicPortalController(mockService, ... , mockHelper.Object); 

     var fakePublicPortalViewModel = new PublicPortalViewModel{RequestNumber = "23"}; 
     bool captchaValid = true; 

     var result = sut.Index(fakePublicPortalViewModel, captchaValid) as JsonResult; 
     dynamic jsonObject = result.Data; 
+0

Hahahahahah,笑確實走。我非常感謝你的回答,但是對於我來說,分離一個預先存在的函數並添加一個新的構造函數重載只是爲了編寫一個成功的測試用例,這對我來說有點新意。我覺得我只是違反了一些編碼原則,以獲得我想要的結果,這是測試的常見做法嗎?本週我剛開始進行自動化單元測試,因此我不確定自己是否在測試驅動開發中獲得了第一堂課,或者如果這是一種不應該在未來完成的解決方法? –

+1

@HunterNelson這可能是那些有爭論的灰色地帶之一。但我發現編寫可測試代碼的最簡單方法是儘可能多地抽象出依賴關係,嘲笑你所知道的事情要容易得多,而不是你不知道的事情。據我所知,這也不違反任何編碼原則,但它最有可能支持SOLID原則中的S,I和D. –