How can I visualize a view in a row in the Visual Studio Unit Testing Framework?

If I create a new MVC 5 project (with unit tests) and create a new superclass for my controllers using a snippet from the popular SO answer, it's easy to visualize the contents of the view in a line:

HomeController.cs

public class HomeController : StringableController { public ActionResult StringIndex() { string result = RenderRazorViewToString("Index", null); return Content(result); } } 

Now, if I am in /Home/StringIndex , I am returning the raw HTML for this view. Carefully (even if not very useful)! But in a .Tests project, if I try to check StringIndex () in a unit test ...

HomeControllerTest.cs

 [TestClass] public class HomeControllerTest { [TestMethod] public void StringIndex() { HomeController controller = new HomeController(); ContentResult result = controller.StringIndex() as ContentResult; string resultString = result.Content; Assert.IsTrue(resultString.Contains("Getting started")); } } 

... no such luck. Calling controller.StringIndex() from unit test raises an ArgumentNullException when System.Web.Mvc.ViewEngineCollection.FindPartialView() is called in the above snippet because controllerContext is null . I tried several Moq approaches (modified versions of SetUpForTest() and MvcMockHelpers ) to mimic controllerContext , but this may not be the right approach, because 1) no approach has been specifically developed for unit testing in Visual Studio, and 2) I'm not really sure it must be real and mock in order to successfully display the view.

Is it possible that in unit tests in Visual Studio to create a controller that RenderRazorViewToString () can work to work?

EDIT to clarify my purpose: I do not want to check the internal workings of RenderRazorViewToString() (which is just the tool used for the job); I want my unit test to be able to parse the actual HTML that will be returned from the controller in the normal case. So if (as a bad, silly example) my Index.cshtml is just <h2>@DateTime.Now.Year</h2> , then Assert.IsTrue(resultString.Contains("<h2>2013</h2> ")); (like the last line in HomeControllerTest.StringIndex() ) will succeed.

+7
unit-testing visual-studio-2013 asp.net-mvc razor vs-unit-testing-framework
source share
2 answers

You can get this method for testing with slight modifications. To test this, you need to change your SUT (System Under Test) so that it becomes more verifiable. It is always useful to change the SUT to make the API more verifiable, even once it looked a little weird.

There are criminals in your SUT that are harder to test. but.

  using (var sw = new StringWriter()) 

b. (inside RenderRazorViewToString)

  ViewEngines.Engines.FindPartialView(ControllerContext, "Index"); 

With StringWriter, you need to access the running StringWriter test so that you have control over what the view of this author was written.

With FindPartialView, ViewEnginesCollection is a static collection in ViewEngines, and FindPartialView has many things that happen at the bottom, and it seems harder to drown. Perhaps there is another way, since FindPartialView is virtual, so we can insert a muted ViewEngine that we could mute the FindPartialView method. But I am not able to stifle / mock the whole universe, so I took a different approach, but still performs this task. This is by introducing a delegate, so I have full control over what FindPartialView returned.

System Test (SUT)

 public class HomeController : Controller { public Func<ViewEngineResult> ViewEngineResultFunc { get; set; } public Func<StringWriter> StringWriterFunc { get; set; } public HomeController() { ViewEngineResultFunc = () => ViewEngines.Engines.FindPartialView(ControllerContext, "Index"); } private string RenderRazorViewToString(string viewName, object model) { ViewData.Model = model; using (var sw = new StringWriter()) { StringWriter stringWriter = StringWriterFunc == null ? sw : StringWriterFunc(); var viewResult = ViewEngineResultFunc(); var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, stringWriter); viewResult.View.Render(viewContext, stringWriter); viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View); return stringWriter.GetStringBuilder().ToString(); } } public ActionResult StringIndex() { string result = RenderRazorViewToString("Index", null); return Content(result); } 

As you can see, there are two delegates for StringWriter, StringWriterFunc is called, and the other is for FindPartialView, which is called ViewEngineResultFunc.

During the actual execution of the program, these delegates should use real instances where, like during the test, they are replaced with fake instances.

Unit test

 [TestClass] public class HomeControllerTest { [TestMethod] public void StringIndex_RenderViewToString_ContentResuleContainsExpectedString() { //Arrange const string viewHtmlContent = "expectedViewContext"; var sut = new HomeController(); var sw = new StringWriter(); var viewEngineResult = SetupViewContent(viewHtmlContent, sw); var controllerContext = new ControllerContext (new Mock<HttpContextBase>().Object, new RouteData(), new Mock<ControllerBase>().Object); sut.ControllerContext = controllerContext; sut.ViewEngineResultFunc = () => viewEngineResult; sut.StringWriterFunc = () => sw; //Act var result = sut.StringIndex() as ContentResult; string resultString = result.Content; //Assert Assert.IsTrue(resultString.Contains(viewHtmlContent)); } private static ViewEngineResult SetupViewContent(string viewHtmlContent, StringWriter stringWriter) { var mockedViewEngine = new Mock<IViewEngine>(); var resultView = new Mock<IView>(); resultView.Setup(x => x.Render(It.IsAny<ViewContext>(), It.IsAny<StringWriter>())) .Callback(() => stringWriter.Write(viewHtmlContent)); var viewEngineResult = new ViewEngineResult (resultView.Object, mockedViewEngine.Object); ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(mockedViewEngine.Object); return viewEngineResult; } } 
+2
source share

An alternative approach to direct HTML analysis would be to use a test environment, such as Selenium WebDriver (from coded unit tests), which will programmatically β€œcontrol” the test page, and you can then write your test statements on the β€œβ€ page using WebDriver to check for elements, element values, etc.

There's a good record of using it from unit tests with MVC and IIS Express here

+1
source share

All Articles