How To Write Unit Test For Method Returning JsonResult With RenderPartialViewToString?
Asked Answered
G

2

3

If you look at the example at this link:

http://www.atlanticbt.com/blog/asp-net-mvc-using-ajax-json-and-partialviews/

How would one write a unit test for the JsonAdd method? I have a similar situation in my own code, but the RenderPartialViewToString errors when calling:

ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView

I've tried different ways of trying to stub that call to no avail. Any help appreciated. Thanks.

Goeselt answered 11/8, 2010 at 19:21 Comment(0)
S
2

Since ViewEninges is a static class, you can't mock it with RhinoMocks. I think your best bet is to create a "partial view renderer" interface. An interface is mockable so you'll be able to stub out the complexity of rendering the view. Here's some quick pseudo-code thrown together.

First, define the partial view renderer interface:

public interface IRenderPartialView
{
    string Render(string viewName, object model);
}

Then, change your base class' RenderPartialViewToString to be the implementation of IRenderPartialView.Render:

public abstract class BaseController : Controller, IRenderPartialView
{
...
    public string Render(string viewName, object model)
    {
        // same code as RenderPartialViewToString
    }
}

Now we need to change your controller constructors so we can inject an IRenderPartialView during testing -- but use the base class one during production. We can accomplish this by using a pair of constructors:

public class YourController : BaseController
{
        private IRenderPartialView partialRenderer;

        public YourController()
        {
            SetRenderer(this);
        }

        public YourController(IRenderPartialView partialRenderer)
        {
            SetRenderer(partialRenderer);
        }

        private void SetRenderer(IRenderPartialView partialRenderer)
        {
            this.partialRenderer = this;
        }
}

Now, JsonAdd can call the partial view renderer:

public JsonResult JsonAdd(AddPersonViewModel AddPersonModel)
{
    ...
    return Json(new
    {
        Success = true,
        Message = "The person has been added!",
        PartialViewHtml = partialRenderer.Render("PersonList", new PersonListViewModel {PersonList = _personList})
    });
}

So, during testing, you'll mock out an IRenderPartialView and send that to the constructor that accepts an IRenderPartialView. During production, when ASP.NET MVC calls your default constructor, it will use the controller as the renderer (which has the implementation of IRenderPartialView.Render inside the base class).

Sifuentes answered 12/8, 2010 at 12:15 Comment(1)
I was in the process of isolating the dependency to an interface, but had not quite gotten to the multiple constructor idea. Works great...thanks Patrick!Goeselt
H
6

I had a lot of trouble to make unit test working with RenderPartialViewToString. I succeeded by doing 2 things. I had to mock the view engine and the controller context.

Here the code :

public ViewEngineResult SetupViewContent(string viewName, string viewHtmlContent)
    {
        var mockedViewEngine = new Mock<IViewEngine>();
        var resultView = new Mock<IView>();

        resultView.Setup(x => x.Render(It.IsAny<ViewContext>(), It.IsAny<TextWriter>()))
            .Callback<ViewContext, TextWriter>((v, t) =>
            {
                t.Write(viewHtmlContent);
            });

        var viewEngineResult = new ViewEngineResult(resultView.Object, mockedViewEngine.Object);
        mockedViewEngine.Setup(x => x.FindPartialView(It.IsAny<ControllerContext>(), viewName, It.IsAny<bool>()))
            .Returns<ControllerContext, string, bool>((controller, view, useCache) =>
            {
                return viewEngineResult;
            });

        mockedViewEngine.Setup(x => x.FindView(It.IsAny<ControllerContext>(), viewName, It.IsAny<string>(), It.IsAny<bool>()))
            .Returns<ControllerContext, string, string, bool>((controller, view, masterName, useCache) =>
            {
                return viewEngineResult;
            });

        ViewEngines.Engines.Clear();
        ViewEngines.Engines.Add(mockedViewEngine.Object);
        return viewEngineResult;
    }

    public void SetContext(ref PointCollecteLivraisonController controller)
    {
        SetupViewContent("MyViewName", "TheViewContent");

        var httpContextBase = new Mock<HttpContextBase>();
        var httpRequestBase = new Mock<HttpRequestBase>();
        var response = new Mock<HttpResponseBase>();
        var session = new Mock<HttpSessionStateBase>();
        var routes = new RouteCollection();
        RouteConfigurator.RegisterRoutes(routes);
        var routeData = new RouteData();
        routeData.Values.Add("controller", "PointCollecteLivraison");
        routeData.Values.Add("action", "RechercheJson");

        httpContextBase.Setup(x => x.Response).Returns(response.Object);
        httpContextBase.Setup(x => x.Request).Returns(httpRequestBase.Object);
        httpContextBase.Setup(x => x.Session).Returns(session.Object);
        session.Setup(x => x["somesessionkey"]).Returns("value");
        httpRequestBase.Setup(x => x.Form).Returns(new NameValueCollection());
        controller.ControllerContext = new ControllerContext(httpContextBase.Object, routeData, controller);
        controller.Url = new UrlHelper(new RequestContext(controller.HttpContext, routeData), routes);
    }

And that is the way i use it all :

PointCollecteLivraisonController controller = new PointCollecteLivraisonController();
SetContext(ref controller);

Here are my sources : View engine mocking : http://thoai-nguyen.blogspot.fr/2011/04/test-mock-mvc-view-engine.html

Controller context mocking : ASP.NET MVC - Unit testing RenderPartialViewToString() with Moq framework?

Hope this help.

Homburg answered 26/7, 2012 at 12:59 Comment(1)
Very good work. For everyone having problem with RouteConfigurator. RegisterRoutes is by default in Global.asax.cs, part of MvcApplication (default) Thank for this code, it helped me a lot.Agentive
S
2

Since ViewEninges is a static class, you can't mock it with RhinoMocks. I think your best bet is to create a "partial view renderer" interface. An interface is mockable so you'll be able to stub out the complexity of rendering the view. Here's some quick pseudo-code thrown together.

First, define the partial view renderer interface:

public interface IRenderPartialView
{
    string Render(string viewName, object model);
}

Then, change your base class' RenderPartialViewToString to be the implementation of IRenderPartialView.Render:

public abstract class BaseController : Controller, IRenderPartialView
{
...
    public string Render(string viewName, object model)
    {
        // same code as RenderPartialViewToString
    }
}

Now we need to change your controller constructors so we can inject an IRenderPartialView during testing -- but use the base class one during production. We can accomplish this by using a pair of constructors:

public class YourController : BaseController
{
        private IRenderPartialView partialRenderer;

        public YourController()
        {
            SetRenderer(this);
        }

        public YourController(IRenderPartialView partialRenderer)
        {
            SetRenderer(partialRenderer);
        }

        private void SetRenderer(IRenderPartialView partialRenderer)
        {
            this.partialRenderer = this;
        }
}

Now, JsonAdd can call the partial view renderer:

public JsonResult JsonAdd(AddPersonViewModel AddPersonModel)
{
    ...
    return Json(new
    {
        Success = true,
        Message = "The person has been added!",
        PartialViewHtml = partialRenderer.Render("PersonList", new PersonListViewModel {PersonList = _personList})
    });
}

So, during testing, you'll mock out an IRenderPartialView and send that to the constructor that accepts an IRenderPartialView. During production, when ASP.NET MVC calls your default constructor, it will use the controller as the renderer (which has the implementation of IRenderPartialView.Render inside the base class).

Sifuentes answered 12/8, 2010 at 12:15 Comment(1)
I was in the process of isolating the dependency to an interface, but had not quite gotten to the multiple constructor idea. Works great...thanks Patrick!Goeselt

© 2022 - 2024 — McMap. All rights reserved.