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

时间:2015-05-11 21:01:03

标签: c# asp.net-mvc nunit moq

好吧所以我试图测试一个控制器结果,以确保它返回正确的值,但我遇到了一些模拟控制器上下文的问题。如果JsonResult成功= true,我想要测试是否有人在页面上输入数字。但是在控制器中,我序列化部分视图并返回一些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();
    }
}

这是我的测试,我为这么混乱而道歉,但我想发布我目前所处的位置。我知道代码多于需要的代码。

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

1 个答案:

答案 0 :(得分:2)

依赖关系,喜欢依赖关系,更好的静态依赖关系。

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

这样的事情:

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;