模拟MVC3 ControllerContext查询字符串集合和ValueProvider

时间:2012-03-30 14:53:03

标签: asp.net-mvc-3 unit-testing mocking query-string value-provider

我在Steve Sanderson's MVC3 book中跟踪了代码示例,其中有一段ControlContext模拟代码,我可以设置表单和查询字符串值。

为了在单元测试中支持TryUpdateModel,我对代码进行了一些更改,例如我已将formValues的数据类型更改为FormCollection formValues,并添加了以下代码以使其正常工作:

        // form Values values setup
        this.Request.Setup(x => x.Form).Returns(formValues);

        // wire up form with value provider
        if (formValues != null)
        {
            onController.ValueProvider = formValues.ToValueProvider();
        }

同时我想对查询字符串集合执行相同的操作,但找不到支持此类函数的等效类。

谁知道如何查询字符串?

1 个答案:

答案 0 :(得分:1)

最终,这是我的解决方案

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Moq;

/// <summary>
/// A helper class for MVC projects' unit testing.
/// </summary>
public class ContextMocks
{
    /// <summary>
    /// Initializes a new instance of the ContextMocks class.
    /// </summary>
    /// <param name="onController">The controller to mock.</param>
    /// <param name="identityName">The fake security identity name for controller.</param>
    /// <param name="queryStrings">Fake query string values of the Http context.</param>
    /// <param name="formValues">Fake form values of the Http context.</param>
    /// <param name="isNewSession">Enables us to mock if the session was created for the current thread or not.</param>
    public ContextMocks(Controller onController, string identityName, FormCollection queryStrings, FormCollection formValues, bool isNewSession = true)
    {
        this.Cookies = new HttpCookieCollection();

        this.Request = new Mock<HttpRequestBase>();

        this.Response = new Mock<HttpResponseBase>();

        this.Server = new Mock<HttpServerUtilityBase>();

        // Define all the common context objects, plus relationships between them
        this.HttpContext = new Mock<HttpContextBase>();
        this.HttpContext.Setup(x => x.Request).Returns(this.Request.Object);
        this.HttpContext.Setup(x => x.Response).Returns(this.Response.Object);
        this.HttpContext.Setup(x => x.Session).Returns(new FakeSessionState(isNewSession));
        this.HttpContext.Setup(x => x.Application).Returns(new FakeApplicationState());
        this.HttpContext.Setup(x => x.User.Identity.Name).Returns(identityName);
        this.HttpContext.Setup(x => x.Server).Returns(this.Server.Object);

        // cookie setup
        this.Request.Setup(x => x.Cookies).Returns(this.Cookies);
        this.Response.Setup(x => x.Cookies).Returns(this.Cookies);

        // query string setup
        this.Request.Setup(x => x.QueryString).Returns(queryStrings);

        // wire up form with value provider
        if (queryStrings != null)
        {
            onController.ValueProvider = queryStrings.ToValueProvider();
        }

        // form Values values setup
        this.Request.Setup(x => x.Form).Returns(formValues);

        // wire up form with value provider
        if (formValues != null)
        {
            onController.ValueProvider = formValues.ToValueProvider();
        }

        // Apply the mock context to the supplied controller instance
        RequestContext rc = new RequestContext(this.HttpContext.Object, new RouteData());
        onController.ControllerContext = new ControllerContext(rc, onController);
    }

    /// <summary>
    /// Initializes a new instance of the ContextMocks class. By using this constructor the mock will also enable UrlHelper mocking
    /// by using routing table setting.
    /// </summary>
    /// <param name="onController">The controller to mock.</param>
    /// <param name="registerRouteTable">
    /// A delegate to the function to register route table. 
    /// Typically it is MvcApplication.RegisterRoutes function in global.asax.cs.
    /// </param>
    /// <param name="identityName">The fake security identity name for controller.</param>
    /// <param name="queryStrings">Fake query string values of the Http context.</param>
    /// <param name="formValues">Fake form values of the Http context.</param>
    /// <param name="isNewSession">Enables us to mock if the session was created for the current thread or not.</param>
    public ContextMocks(
        Controller onController, 
        Action<RouteCollection> registerRouteTable, 
        string identityName, 
        FormCollection queryStrings, 
        FormCollection formValues,
        bool isNewSession = true)
        : this(onController, identityName, queryStrings, formValues, isNewSession)
    {
        this.Response.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(s => s);

        // Arrange (get the routing config and test context)
        RouteCollection routeConfig = new RouteCollection();
        registerRouteTable(routeConfig);
        onController.Url = new UrlHelper(new RequestContext(this.HttpContext.Object, new RouteData()), routeConfig);
    }

    /// <summary>
    /// Gets Http Context mock.
    /// </summary>
    public Mock<HttpContextBase> HttpContext { get; private set; }

    /// <summary>
    /// Gets Http request mock.
    /// </summary>
    public Mock<HttpRequestBase> Request { get; private set; }

    /// <summary>
    /// Gets Http response mock.
    /// </summary>
    public Mock<HttpResponseBase> Response { get; private set; }

    /// <summary>
    /// Gets the mock server object.
    /// </summary>
    public Mock<HttpServerUtilityBase> Server { get; private set; }

    /// <summary>
    /// Gets Route data.
    /// </summary>
    public RouteData RouteData { get; private set; }

    /// <summary>
    /// Gets or sets A collection used to hold fake cookie values.
    /// </summary>
    private HttpCookieCollection Cookies
    {
        get;
        set;
    }

    /// <summary>
    /// Use queryStringCollectionProvider fake HttpSessionStateBase, because it's hard to mock it with Moq.
    /// </summary>
    private class FakeSessionState : HttpSessionStateBase
    {
        /// <summary>
        /// Enables us to mock whether the session was created for the current thread or not.
        /// </summary>
        private bool isNewSession;

        /// <summary>
        /// Fake session state collection.
        /// </summary>
        private Dictionary<string, object> items = new Dictionary<string, object>();

        /// <summary>
        /// Initializes a new instance of the FakeSessionState class.
        /// </summary>
        /// <param name="isNewSession">Enables us to mock if the session was created for the current thread or not.</param>
        public FakeSessionState(bool isNewSession)
        {
            this.isNewSession = isNewSession;
        }           

        /// <summary>
        /// Gets a value that indicates whether the session was created during the current request.
        /// </summary>
        public override bool IsNewSession
        {
            get
            {
                return this.isNewSession;
            }
        }

        /// <summary>
        /// An indexer to access the fake session item.
        /// </summary>
        /// <param name="name">Name of fake session key.</param>
        /// <returns>Fake session value.</returns>
        public override object this[string name]
        {
            get { return this.items.ContainsKey(name) ? this.items[name] : null; }
            set { this.items[name] = value; }
        }

        /// <summary>
        /// Empties the contents of the session.
        /// </summary>
        public override void Abandon()
        {
            this.items = new Dictionary<string, object>();
        }
    }

    /// <summary>
    /// Use queryStringCollectionProvider fake HttpApplicationStateBase, because it's hard to mock it with Moq.
    /// </summary>
    private class FakeApplicationState : HttpApplicationStateBase
    {
        /// <summary>
        /// Fake application state collection.
        /// </summary>
        private Dictionary<string, object> items = new Dictionary<string, object>();

        /// <summary>
        /// An indexer to access the fake application item.
        /// </summary>
        /// <param name="name">Name of fake application key.</param>
        /// <returns>Fake application value.</returns>
        public override object this[string name]
        {
            get { return this.items.ContainsKey(name) ? this.items[name] : null; }
            set { this.items[name] = value; }
        }

        /// <summary>
        /// Mock out of the lock method.
        /// </summary>
        public override void Lock()
        {
        }

        /// <summary>
        /// Mock out of the unlock method.
        /// </summary>
        public override void UnLock()
        {
        }

        /// <summary>
        /// Adds the fake object to the application object.
        /// </summary>
        /// <param name="name">The key</param>
        /// <param name="value">The value being added in conjunction with the key.</param>
        public override void Add(string name, object value)
        {
            this.items.Add(name, value);
        }
    }
}