Как написать модульный тест для метода, возвращающего JsonResult с помощью RenderPartialViewToString?

Если вы посмотрите на пример по этой ссылке:

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

Как написать модульный тест для метода JsonAdd? У меня аналогичная ситуация в собственном коде, но при вызове ошибки RenderPartialViewToString:

ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView

Я пробовал разными способами попытаться заглушить этот звонок, но безрезультатно. Любая помощь приветствуется. Спасибо.


person Shawn    schedule 11.08.2010    source источник


Ответы (2)


Поскольку ViewEninges - статический класс, вы не можете имитировать его с помощью RhinoMocks. Я думаю, что лучше всего создать интерфейс «рендерера частичного представления». Интерфейс можно смоделировать, поэтому вы сможете избавиться от сложности рендеринга представления. Вот небольшой псевдокод.

Сначала определите интерфейс средства визуализации частичного представления:

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

Затем измените базовый класс RenderPartialViewToString на реализацию IRenderPartialView.Render:

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

Теперь нам нужно изменить конструкторы ваших контроллеров, чтобы мы могли внедрить IRenderPartialView во время тестирования, но использовать базовый класс во время производства. Мы можем добиться этого с помощью пары конструкторов:

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;
        }
}

Теперь JsonAdd может вызвать средство визуализации частичного представления:

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

Итак, во время тестирования вы создадите макет IRenderPartialView и отправите его конструктору, который принимает IRenderPartialView. Во время производства, когда ASP.NET MVC вызывает ваш конструктор по умолчанию, он будет использовать контроллер в качестве средства визуализации (которое имеет реализацию IRenderPartialView.Render внутри базового класса).

person PatrickSteele    schedule 12.08.2010
comment
Я находился в процессе изоляции зависимости от интерфейса, но не совсем понял идею множественного конструктора. Отлично работает ... спасибо, Патрик! - person Shawn; 12.08.2010

У меня было много проблем с тем, чтобы юнит-тест работал с RenderPartialViewToString. Мне удалось сделать 2 вещи. Мне пришлось издеваться над движком представления и контекстом контроллера.

Вот код:

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);
    }

И вот как я все это использую:

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

Вот мои источники: Просмотр имитации движка: http://thoai-nguyen.blogspot.fr/2011/04/test-mock-mvc-view-engine.html

Мокинг контекста контроллера: ASP.NET MVC - модульное тестирование RenderPartialViewToString () с фреймворком Moq?

Надеюсь на эту помощь.

person Charles Martin    schedule 26.07.2012
comment
Очень хорошая работа. Для всех, у кого проблемы с RouteConfigurator. RegisterRoutes по умолчанию находится в Global.asax.cs, как часть MvcApplication (по умолчанию). Спасибо за этот код, он мне очень помог. - person Mariusz.W; 06.08.2012