2015-05-11 3 views
1

Хорошо, поэтому я пытаюсь проверить результат контроллера, чтобы убедиться, что он возвращает правильные значения, но я столкнулся с рядом проблем с насмешливыми контекстами контроллера. Все, что я хочу сделать, это проверить, входит ли кто-нибудь на страницу на странице, если JsonResult имеет успех = true. Однако в контроллере я сериализую частичный вид и возвращаю некоторый HTML, содержащий все данные, относящиеся к номеру запроса. Мне комфортно настраивать ожидаемые результаты в результате json, но я даже не могу пройти тест до этого. При сериализации частичного обзора тест зависает. Мне все равно не нужно проверять это, но я не знаю, как работать с результатом этой функции, кроме создания некоторых подделок для контекста controller, и пусть он ищет сериализацию представления.Как протестировать действие контроллера, которое сериализует представление

Вот это контроллер

[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

Зависимости, Gotta зависимости любовь, еще лучше статические зависимости.

Если исходный код в 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, действительно смеясь. Я очень благодарен за ваш ответ, однако для меня немного новое, чтобы разделить ранее существовавшую функцию и добавить новую перегрузку конструктора, чтобы написать успешный тестовый пример. Я чувствую, что я просто нарушил некоторые принципы кодирования, чтобы получить результат, который я хотел, это обычная практика тестирования? Я только начал с автоматизированного тестирования модулей на этой неделе, поэтому я не уверен, что я только что получил свой первый урок в Test Driven Development или если это одно решение, которое не должно быть сделано в будущем? –

+1

@HunterNelson это, вероятно, одна из тех серых областей, где есть аргументы в любом случае. Но я нашел самый простой способ написать тестируемый код, чтобы как можно больше абстрагировать зависимости, гораздо проще издеваться над чем-то, о чем вы знаете, чем о чем-то, чего у вас нет. Насколько я знаю, это также не нарушает каких-либо принципов кодирования, но, скорее всего, поддерживает принципы S, I и D в принципах SOLID. –

Смежные вопросы