2015-01-22 14 views
7

Pracuję nad aplikacją sieci Web ASP.NET MVC 4, która generuje duże i skomplikowane raporty. Chcę pisać testy jednostkowe, które wygenerować widok, aby upewnić się, że widok nie wysadzić w zależności od modelu:Renderuj widok podczas testu jednostkowego - ControllerContext.DisplayMode

[Test] 
public void ExampleTest(){     
    var reportModel = new ReportModel(); 

    var reportHtml = RenderRazorView(
      @"..\..\Report.Mvc\Views\Report\Index.cshtml", 
      reportModel); 

    Assert.IsFalse(
     string.IsNullOrEmpty(reportHtml), 
     "View Failed to Render!");   
} 

public string RenderRazorView(string viewPath, object model){ 
    //WHAT GOES HERE? 
} 

widziałem wiele informacji na ten temat w całym Internecie, ale to albo argumentowanie przeciwko testowaniu lub może być użyte tylko w kontekście żądania internetowego.

  • polemizując - Unit Testing the Views? - To kończy nie powinno być żadnej logiki w Widoku więc należy tylko trzeba przetestować kompilację. Wydaje mi się, że warto przetestować View, aby upewnić się, że nie ma wyjątków Null Reference, pokazano odpowiednie sekcje, itp.
  • Kontekst żądania sieci - Render a view as a string - Ma to na celu renderowanie widoku, który ma być wysłany e-mail. Ale takie podejście wymaga wywołania za pośrednictwem żądania internetowego (tj. Ważnego HttpContextBase).

pracuję dostosować Render a view as a string pracować z szydzili HttpContextBase, ale zostały uruchomione na problemy podczas korzystania z szydzili ControllerContext:

Odwołanie do obiektu nie zostało ustawione na wystąpienie obiektu. w System.Web.WebPages.DisplayModeProvider.GetDisplayMode (HttpContextBase kontekst) w System.Web.Mvc.ControllerContext.get_DisplayMode() w System.Web.Mvc.VirtualPathProviderViewEngine.GetPath (ControllerContext controllerContext, ciąg [String] lokalizacjach [ ] areaLocations, String locationsPropertyName, String name, String controllerName, String cacheKeyPrefix, Boolean useCache, String [] & searchedLocations)

jest to kod mam tak daleko:

public string RenderRazorView(string viewPath, object model) 
    { 
     var controller = GetMockedDummyController(); 

     //Exception here 
     var viewResult = 
      ViewEngines.Engines.FindView(controller.ControllerContext, "Index", ""); 

     using (var sw = new StringWriter()) 
     { 
      var viewContext = 
       new ViewContext(
        controller.ControllerContext, 
        viewResult.View, 
        new ViewDataDictionary(model), 
        new TempDataDictionary(), 
        sw); 

      viewResult.View.Render(viewContext, sw); 

      return sw.ToString(); 
     } 
    } 

buduję Kontroler:

private Controller GetMockedDummyController() 
    { 
     var HttpContextBaseMock = new Mock<HttpContextBase>(); 
     var HttpRequestMock = new Mock<HttpRequestBase>(); 
     var HttpResponseMock = new Mock<HttpResponseBase>(); 
     HttpContextBaseMock.SetupGet(x => x.Request).Returns(HttpRequestMock.Object); 
     HttpContextBaseMock.SetupGet(x => x.Response).Returns(HttpResponseMock.Object); 

     var controller = new DummyController(); 

     var routeData = new RouteData(); 
     routeData.Values.Add("controller", "Dummy"); 

     controller.ControllerContext = 
      new ControllerContext(
       HttpContextBaseMock.Object, 
       routeData, 
       controller); 

     controller.Url = 
      new UrlHelper(
       new RequestContext(
        HttpContextBaseMock.Object, 
        routeData), 
       new RouteCollection()); 

     return controller; 
    } 

DummyController tylko public class DummyController : Controller {}

Pytanie

podać ścieżkę do widoku, w jaki sposób można uczynić go do HTML z projektu test? A dokładniej, jak mogę wyśmiewać ControllerContext.DisplayMode?

+0

Mam podobny problem. Czy w końcu znalazłeś jakieś rozwiązanie? Zastanawiam się również, jak wyśmiać ControllerContext.DisplayMode. – Shahin

+2

Niestety, nigdy nie znalazłem dobrego rozwiązania, aby to zrobić i jeśli dobrze pamiętam, musiałem porzucić mój wysiłek, kiedy dmuchałem przez mój Spike z badań i nie wróciłem z niczym użytecznym. Możesz rzucić okiem na niektóre z nowych rzeczy ASP.NET Core, ponieważ historia testów może być znacznie lepsza. Częścią tego, co zrobili, AFAIK jest izolowanie 'HttpContext', co było jednym z podstawowych problemów. –

Odpowiedz

4

Zakładając, że masz kompletny rozdział obaw, czy konieczne jest utworzenie instancji kontrolera? Jeśli nie, to możesz użyć RazorEngine, aby przetestować swoje poglądy.

var contents = File.ReadAllText("pathToView"); 
var result = Razor.Parse(contents,model); 
// assert here 
+0

Nie, nie muszę inwokować kontrolera ani jego metod. Mam już model i chcę go użyć do renderowania widoku, więc spróbuję! –

+0

Czy użyłeś tego do renderowania widoków "MVC". Nie mogę zmusić go do kompilowania instrukcji takich jak '@ Styles.Render()'. Silnik narzeka, że ​​brakuje złożeń, mimo że je dodałem. Po przeanalizowaniu skompilowanego wyniku wygląda na to, że instrukcje są dodawane w oparciu o web.config 'system.web \ pages \ namespaces'. Czy istnieje sposób na ich dodanie? –

+0

W łączu RazorEngine znajduje się dokumentacja omawiająca sposób obsługi złożeń. Tak, użyłem tego dla widoków i szablonów ciągów. Nie użyłem go jednak do renderowania szablonów zagnieżdżonych. – B2K

0

Trzeba będzie pusty Controller tylko do testowania (EX TestController)

public class WebMvcHelpers 
{ 
    public static string GetViewPageHtml(object model, string viewName) 
    { 
     System.Web.Mvc.Controller controller = CreateController<TestController>(); 

     ViewEngineResult result = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName); 

     if (result.View == null) 
      throw new Exception(string.Format("View Page {0} was not found", viewName)); 

     controller.ViewData.Model = model; 
     StringBuilder sb = new StringBuilder(); 
     using (StringWriter sw = new StringWriter(sb)) 
     { 
      using (System.Web.UI.HtmlTextWriter output = new System.Web.UI.HtmlTextWriter(sw)) 
      { 
       ViewContext viewContext = new ViewContext(controller.ControllerContext, result.View, controller.ViewData, controller.TempData, output); 
       result.View.Render(viewContext, output); 
      } 
     } 

     return sb.ToString(); 
    } 

    /// <summary> 
    /// Creates an instance of an MVC controller from scratch 
    /// when no existing ControllerContext is present  
    /// </summary> 
    /// <typeparam name="T">Type of the controller to create</typeparam> 
    /// <returns></returns> 
    public static T CreateController<T>(RouteData routeData = null) 
       where T : System.Web.Mvc.Controller, new() 
    { 
     T controller = new T(); 

     // Create an MVC Controller Context 
     HttpContextBase wrapper = null; 
     if (HttpContext.Current != null) 
      wrapper = new HttpContextWrapper(System.Web.HttpContext.Current); 
     //else 
     // wrapper = CreateHttpContextBase(writer); 


     if (routeData == null) 
      routeData = new RouteData(); 

     if (!routeData.Values.ContainsKey("controller") && !routeData.Values.ContainsKey("Controller")) 
      routeData.Values.Add("controller", controller.GetType().Name 
                 .ToLower() 
                 .Replace("controller", "")); 

     controller.ControllerContext = new System.Web.Mvc.ControllerContext(wrapper, routeData, controller); 
     return controller; 
    } 
} 

public class TestController : Controller 
{ 
    public ActionResult Index() 
    { 
     return View(); 
    } 
} 
+0

To nie jest szewem do pracy. Nadal potrzebuje 'HttpContext.Current'. Dlaczego skomentowano metodę "CreateHttpContextBase"? Czy masz na to implementację? –

+0

Hmm, nie, nie wiem skąd pochodzi ta metoda. Ale [możesz wykpić] (http://stackoverflow.com/questions/4379450/mock-httpcontext-current-in-test-init-method) instancji 'HttpContext.Current' – Catalin

Powiązane problemy