0

All,

I'm developing and unit testing an interactive voice application using ASP.NET MVC 3 whose controllers return Views containing VoiceXML. I'd like to create unit tests that capture the actual VoiceXML output so I can schema-validate it.

My reading and testing have taken me to Scott H's FakeHttpContext that uses Moq, as well as several responses here. Everything compiles correctly, and I'm trying to do something like the following:

[TestMethod]
public void WelcomeTest1()
{
     EmergencyController controller = new EmergencyController();
     controller.ControllerContext = new  ControllerContext(MvcMockHelpers.FakeHttpContext("~/Emergency/Welcome"), new RouteData(), controller);

     ViewResult result = (controller.Welcome()) as ViewResult;
 .
 .
     Assert.IsTrue(controller.ControllerContext.HttpContext.Response.OutputStream.Length > 0);
     // assert schema validation on the output here
}

However, stepping through this, I can see that the Welcome view being called, but I'm looking for something in the Response.Output and not finding anything. The mock is set up as follows, in hope that setting CallBase to true would actually write something out. I found some code that I added to the FakeHttpContext constructor that supposedly invokes a StringWriter, but to no avail:

public static HttpContextBase FakeHttpContext()
{
    var context = new Mock<HttpContextBase>();
    var request = new Mock<HttpRequestBase>() { CallBase = true };
    var response = new Mock<HttpResponseBase>();
    var session = new Mock<HttpSessionStateBase>();
    var server = new Mock<HttpServerUtilityBase>();

    context.Setup(ctx => ctx.Request).Returns(request.Object);
    context.Setup(ctx => ctx.Response).Returns(response.Object);
    context.Setup(ctx => ctx.Session).Returns(session.Object);
    context.Setup(ctx => ctx.Server).Returns(server.Object);
    response.Setup(r => r.OutputStream).Returns(new MemoryStream());
    response.Setup(r => r.Headers).Returns(new NameValueCollection());           

    var writer = new StringWriter();
    var wr = new SimpleWorkerRequest("", "", "", "", writer);        
    HttpContext.Current = new HttpContext(wr);

    return context.Object;
}

I'm sure I'm missing something obvious, but I'm stumped right now.

Thanks

Jim Stanley

Blackboard Connect

Jim Stanley
  • 146
  • 1
  • 3
  • 11

1 Answers1

0

The result doesn't get populated in the ViewResult. In other words, the view isn't rendered by you calling return View() in your controller, rather mvc takes the viewresult and renders it at a later time in the request-sycle. What you need to do is to create a render-function for ViewResults, like this one:

using System;
using System.IO;
using System.Web.Mvc;

namespace CoPrice.Helpers
{
    public static class ViewRendrer
    {
        public static string ToHtml(this ViewResult result, Controller controller)
        {
            controller.ViewData.Model = result.Model;
            try
            {
                using (StringWriter sw = new StringWriter())
                {
                    ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, result.ViewName);
                    ViewContext context = new ViewContext(controller.ControllerContext, viewResult.View, result.ViewData, result.TempData, sw);
                    viewResult.View.Render(context, sw);

                    return sw.GetStringBuilder().ToString();
                }
            }
            catch (Exception e)
            {
                return e.ToString();
            }
        }
    }
}

Then you can do result.ToHtml(controller) to get the actual data (this works for RazorViews only I think, though I'm not sure, that's what I've used it for at least).

Alxandr
  • 12,345
  • 10
  • 59
  • 95
  • Hmmmm, almost - but the following code bombs out because the controller context is null: [TestMethod] public void WelcomeTest1() { EmergencyController controller = new EmergencyController(); ViewResult result = (controller.Welcome()) as ViewResult; string xml = ViewRenderer.ToHtml(result, controller); Assert.IsTrue(xml.Length > 0); } I also tried adding the FakeControllerContext() code from some of the other places I've visited, but it also bombs because the RouteData() part of the controller context is just a new RouteData(). – Jim Stanley May 13 '11 at 01:16
  • Oh, right. I had that problem once too... You need to a LOT of mocking to get it all working if I'm not mistaken, but it is indeed possible. I don't have the code for that and nor the will/time (sorry) to rewrite it, cause it was a lot of work, but if you do some google'ing, you might find an answer. I use this ToHtml-function inside my controllers you see, and there it works out as it should. – Alxandr May 13 '11 at 02:13
  • Thanks. I suspect (hope) that the RouteValues() would be the final thing I need. Hopefully I can get the actual route values from the application. Thanks *much* for the extension method - always good to bump up my knowledge. – Jim Stanley May 13 '11 at 16:29
  • No problem. I just found it online a couple of days ago too, though I don't remember where xD. Think it was here no stack-overflow though, I just googled rendering of razor-views I think. – Alxandr May 13 '11 at 23:48