Как да напиша Unit Test за метод, връщащ 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