如何测试序列化视图的控制器操作

How to test controller action that serializes a view

好吧,所以我正在尝试测试控制器结果以确保它 return 设置了正确的值,但我 运行 遇到了一些模拟控制器上下文的问题。如果 JsonResult 为 success = true,我只想测试是否有人在页面上输入了数字。但是在控制器中,我序列化了一个部分视图和 return 一些 HTML,其中包含与请求编号相关的所有数据。我很乐意在 json 结果中设置预期结果,但我什至无法让测试推进到那么远。序列化局部视图时测试挂起。我不太关心测试它,但我不知道如何解决该函数的结果,除了为 controllercontext 创建一些假货并让它搜索序列化视图。

这是控制器

[HttpPost]
[RecaptchaControlMvc.CaptchaValidator]
public JsonResult GetPublicInformation(PublicPortalViewModel model, bool captchaValid)
{
    if (captchaValid == true)
    {
        //check to see if their is a request number to look up the request by
        if (model.RequestNumber != null)
        {
            //fill model data using by calling the service
            model = _service.GetPublicPortalData(model);
            var content = base.SerializeView("DisplayPublicInformation", model);
            return Json(new { success = true, htmlContent = content });
        }
        else
        {
            return Json(new { success = false, htmlContent = "<span style=\"color: red;\">No request was found with that number, please enter a valid request number.</span>" });
        }
    }
    else
    {
        return Json(new { success = false, htmlContent = "<span style=\"color: red;\">Please enter a valid Captcha Value</span>" });
    }
}

具体来说,我遇到了这条线的问题:

var content = base.SerializeView("DisplayPublicInformation", model);

下面是该函数的定义:

protected internal virtual string SerializeView(string viewName, object model)
{
    if (string.IsNullOrEmpty(viewName))
        viewName = ControllerContext.RouteData.GetRequiredString("action");

    this.ViewData.Model = model;

    using (var sw = new StringWriter())
    {
        // keep getting null reference errors on this line when I write my tests .
        var viewResult = ViewEngines.Engines.FindPartialView(this.ControllerContext, viewName);
        var viewContext = new ViewContext(this.ControllerContext, viewResult.View, this.ViewData, this.TempData, sw);
        // render the view into the stringwriter class
        viewResult.View.Render(viewContext, sw);
        // output the rendered string
        return sw.GetStringBuilder().ToString();
    }
}

这是我的测试,对于如此混乱,我深表歉意,但我想 post 我目前所在的位置。我知道代码比需要的多。

private PartialViewResult _result;
private Mock<HttpContextBase> _mockHttpContext;
private HttpContextBase _httpContext;
private RouteData _routeData;
private RouteData _parentRouteData;

protected Mock<HttpContextBase> HttpContextBaseMock;
protected Mock<HttpRequestBase> HttpRequestMock;
protected Mock<HttpResponseBase> HttpResponseMock;

[Test]
public void GetPublicInformationValidRequestNumber()
{
    var sut = new PublicPortalController();
    SetupRouteData();
    HttpContextBaseMock = new Mock<HttpContextBase>();
    HttpContextBaseMock.SetupAllProperties();
    HttpRequestMock = new Mock<HttpRequestBase>();
    HttpResponseMock = new Mock<HttpResponseBase>();
    HttpContextBaseMock.SetupGet(x => x.Request).Returns(HttpRequestMock.Object);
    HttpContextBaseMock.SetupGet(x => x.Response).Returns(HttpResponseMock.Object);

    var browser = new Mock<HttpBrowserCapabilitiesBase>(MockBehavior.Strict);
    var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
    var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
    var session = new Mock<HttpSessionStateBase>(MockBehavior.Strict);
    var server = new Mock<HttpServerUtilityBase>(MockBehavior.Strict);
    var cookies = new HttpCookieCollection();
    var items = new ListDictionary();

    var mockViewEngine = new Mock<IViewEngine>();

    Mock<IView> view = new Mock<IView>();
    var viewResult = new ViewEngineResult(new[] { "location1", "location2" });

    mockViewEngine
        .Setup(x => x.FindView(It.IsAny<ControllerContext>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
        .Returns(viewResult);

    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(mockViewEngine.Object);

    browser.Setup(b => b.IsMobileDevice).Returns(false);
    request.Setup(r => r.Cookies).Returns(cookies);
    request.Setup(r => r.ValidateInput());
    request.Setup(r => r.UserAgent).Returns("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11");
    response.Setup(r => r.Cookies).Returns(cookies);

    request.Setup(r => r.Browser).Returns(browser.Object);
    HttpContextBaseMock.Setup(ctx => ctx.Items).Returns(items);

    var routes = new RouteCollection();

    var ControllerContext = new Mock<ControllerContext>(HttpContextBaseMock.Object, _routeData, sut);

    var controller = new Mock<PublicPortalController>();

    ControllerContext.SetupGet(c => c.Controller).Returns(controller.Object);
    ControllerContext.SetupGet(c => c.HttpContext).Returns(HttpContextBaseMock.Object);

    sut.Url = new UrlHelper(new RequestContext(HttpContextBaseMock.Object, _routeData), routes);
    sut.ControllerContext = ControllerContext.Object;
    var basecontroller = new Mock<RequestITBaseController>();

    var fakePublicPortalViewModel = new Mock<PublicPortalViewModel>();
    fakePublicPortalViewModel.Setup(m => m.RequestNumber).Returns("23");
    bool captchaValid = true;

    basecontroller.Setup(c => c.SerializeView("DisplayPublicInformation", fakePublicPortalViewModel)).Returns("");

    var result = sut.GetPublicInformation(fakePublicPortalViewModel.Object, captchaValid) as JsonResult;
    dynamic jsonObject = result.Data;

    Assert.IsTrue(jsonObject.success);
}

private void SetupRouteData()
{
    SetupParentRouteData();
    var viewContext = new ViewContext { RouteData = _parentRouteData };

    _routeData = new RouteData();
    _routeData.Values.Add("controller", "PublicPortalController");
    _routeData.Values.Add("action", "GetPublicInformation");
    _routeData.DataTokens["ParentActionViewContext"] = viewContext;
}

private void SetupParentRouteData()
{
    _parentRouteData = new RouteData();
    _parentRouteData.Values.Add("controller", "PublicPortalController");
    _parentRouteData.Values.Add("action", "Index");
}

依赖关系,一定要喜欢依赖关系,更好的是静态依赖关系。

如果 SerializeView 中的底层代码对​​于单元测试不是那么重要,那么最简单的做法是将该方法抽象为另一种类型并注入而不是从基础继承它 class.....

像这样:

public interface IViewHelper
{
    string SerializeView(ControllerContext context, ViewDataDictionary viewData, TempDataDictionary tempData,
        string viewName, object model);
}    

public class ViewHelper : IViewHelper
{
    private readonly ViewEngineCollection _viewEngines = ViewEngines.Engines;

    public string SerializeView(ControllerContext context, ViewDataDictionary viewData, TempDataDictionary tempData, string viewName, object model)
    {
        viewData.Model = model;
        using (var sw = new StringWriter())
        {
            // keep getting null reference errors on this line when I write my tests .
            var viewResult = _viewEngines.FindPartialView(context, viewName);
            var viewContext = new ViewContext(context, viewResult.View, viewData, tempData, sw);
            // render the view into the stringwriter class
            viewResult.View.Render(viewContext, sw);
            // output the rendered string
            return sw.GetStringBuilder().ToString();
        }
    }
}

然后在你的控制器中:

public PublicPortalController(..., IViewHelper helper)
{
    _helper = helper;
}

....

public JsonResult GetPublicInformation(PublicPortalViewModel model, bool captchaValid)
{
    ....
            //fill model data using by calling the service
            model = _service.GetPublicPortalData(model);
            var content = _helper.SerializeView(ControllerContext, ViewData, TempData, "DisplayPublicInformation", model);
            return Json(new { success = true, htmlContent = content });
   ....
}

现在在你的测试中,你所要做的就是模拟 IViewHelper,你应该笑了:

var mockHelper = new Mock<IViewHelper>();
        mockHelper.Setup(
            x =>
                x.SerializeView(It.IsAny<ControllerContext>(), It.IsAny<ViewDataDictionary>(),
                    It.IsAny<TempDataDictionary>(), "", It.IsAny<PublicPortalViewModel>())).Returns("<html></html>");

        var sut = new PublicPortalController(mockService, ... , mockHelper.Object);

        var fakePublicPortalViewModel = new PublicPortalViewModel{RequestNumber = "23"};
        bool captchaValid = true;

        var result = sut.Index(fakePublicPortalViewModel, captchaValid) as JsonResult;
        dynamic jsonObject = result.Data;