首页 文章

如何使用Moq模拟会话对象集合

提问于
浏览
42

我正在使用shanselmann's MvcMockHelper类使用Moq模拟一些HttpContext的东西,但我遇到的问题是能够在我的MVC控制器中为我的模拟会话对象分配一些内容然后能够在我的单元测试中读取相同的值以进行验证 .

我的问题是如何将存储集合分配给模拟的会话对象,以允许诸如session [“UserName”] =“foo”之类的代码保留“foo”值并使其在单元测试中可用 .

6 回答

  • 3

    我从Scott Hanselman的MVCMockHelper开始,添加了一个小类并进行了如下所示的修改,以允许控制器正常使用Session和单元测试来验证控制器设置的值 .

    /// <summary>
    /// A Class to allow simulation of SessionObject
    /// </summary>
    public class MockHttpSession : HttpSessionStateBase
    {
        Dictionary<string, object> m_SessionStorage = new Dictionary<string, object>();
    
        public override object this[string name]
        {
            get { return m_SessionStorage[name]; }
            set { m_SessionStorage[name] = value; }
        }
    }
    
    //In the MVCMockHelpers I modified the FakeHttpContext() method as shown below
    public static HttpContextBase FakeHttpContext()
    {
        var context = new Mock<HttpContextBase>();
        var request = new Mock<HttpRequestBase>();
        var response = new Mock<HttpResponseBase>();
        var session = new MockHttpSession();
        var server = new Mock<HttpServerUtilityBase>();
    
        context.Setup(ctx => ctx.Request).Returns(request.Object);
        context.Setup(ctx => ctx.Response).Returns(response.Object);
        context.Setup(ctx => ctx.Session).Returns(session);
        context.Setup(ctx => ctx.Server).Returns(server.Object);
    
        return context.Object;
    }
    
    //Now in the unit test i can do
    AccountController acct = new AccountController();
    acct.SetFakeControllerContext();
    acct.SetBusinessObject(mockBO.Object);
    
    RedirectResult results = (RedirectResult)acct.LogOn(userName, password, rememberMe, returnUrl);
    Assert.AreEqual(returnUrl, results.Url);
    Assert.AreEqual(userName, acct.Session["txtUserName"]);
    Assert.IsNotNull(acct.Session["SessionGUID"]);
    

    它并不完美,但它足以进行测试 .

  • 0

    在这里使用Moq 3.0.308.2是我的单元测试中的帐户控制器设置示例:

    private AccountController GetAccountController ()
        {
          .. setup mocked services..
    
          var accountController = new AccountController (..mocked services..);
    
          var controllerContext = new Mock<ControllerContext> ();
          controllerContext.SetupGet(p => p.HttpContext.Session["test"]).Returns("Hello World");
          controllerContext.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(_testEmail);
          controllerContext.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
          controllerContext.SetupGet(p => p.HttpContext.Response.Cookies).Returns(new HttpCookieCollection ());
    
          controllerContext.Setup (p => p.HttpContext.Request.Form.Get ("ReturnUrl")).Returns ("sample-return-url");
          controllerContext.Setup (p => p.HttpContext.Request.Params.Get ("q")).Returns ("sample-search-term");
    
          accountController.ControllerContext = controllerContext.Object;
    
          return accountController;
        }
    

    然后在你的控制器方法中,以下内容应返回“Hello World”

    string test = Session["test"].ToString ();
    
  • 32

    我做了一个比@RonnBlack发布的答案稍微复杂一点的模拟

    public class HttpSessionStateDictionary : HttpSessionStateBase
    {
        private readonly NameValueCollection keyCollection = new NameValueCollection();
    
        private readonly Dictionary<string, object> _values = new Dictionary<string, object>();
    
        public override object this[string name]
        {
            get { return _values.ContainsKey(name) ? _values[name] : null; }
            set { _values[name] = value; keyCollection[name] = null;}
        }
    
        public override int CodePage
        {
            get { throw new NotImplementedException(); }
            set { throw new NotImplementedException(); }
        }
    
        public override HttpSessionStateBase Contents
        {
            get { throw new NotImplementedException(); }
        }
    
        public override HttpCookieMode CookieMode
        {
            get { throw new NotImplementedException(); }
        }
    
        public override int Count
        {
            get { return _values.Count; }
        }
    
         public override NameObjectCollectionBase.KeysCollection Keys
    {
        get { return keyCollection.Keys; }
    }
    
        public Dictionary<string, object> UnderlyingStore
        {
            get { return _values; }
        }
    
        public override void Abandon()
        {
            _values.Clear();
        }
    
        public override void Add(string name, object value)
        {
            _values.Add(name, value);
        }
    
        public override void Clear()
        {
            _values.Clear();
        }
    
        public override void CopyTo(Array array, int index)
        {
            throw new NotImplementedException();
        }
    
        public override bool Equals(object obj)
        {
            return _values.Equals(obj);
        }
    
        public override IEnumerator GetEnumerator()
        {
            return _values.GetEnumerator();
        }
    
        public override int GetHashCode()
        {
            return (_values != null ? _values.GetHashCode() : 0);
        }
    
        public override void Remove(string name)
        {
            _values.Remove(name);
        }
    
        public override void RemoveAll()
        {
            _values.Clear();
        }
    
        public override void RemoveAt(int index)
        {
            throw new NotImplementedException();
        }
    
        public override string ToString()
        {
            return _values.ToString();
        }
    
        public bool Equals(HttpSessionStateDictionary other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Equals(other._values, _values);
        }
    }
    
  • 0

    我刚刚找到了一个很好的例子,说明Oxite团队如何伪造他们的HttpSessionState并在该伪造内部维护一个SessionStateItemCollection集合 . 在我的情况下,这应该和moq一样好用 .

    编辑:

    此示例的URL为http://oxite.codeplex.com/sourcecontrol/changeset/view/33871?projectName=oxite#388065

  • 2

    我认为你可以在模拟上设置一个期望值,它具有一个特定的值,它应该返回任何值 . 模拟不是用作实际的假货,而是可以断言行为的东西 .

    听起来你实际上正在寻找一个可以绕过会话的适配器,你可以在测试期间提供不同的实现,在运行时它会返回HttpContext会话项吗?

    这有意义吗?

  • 60

    谢谢@RonnBlack的解决方案!在我的情况下,我一直得到这个异常,因为Session.SessionID为null:

    System.NotImplementedException was unhandled by user code
      HResult=-2147467263
      Message=The method or operation is not implemented.
      Source=System.Web
      StackTrace:
           at System.Web.HttpSessionStateBase.get_SessionID()
    

    为了解决这个问题,我使用Moq Mock<HttpSessionStateBase> 而不是他的MockHttpSession来实现@ RonnBlack的代码:

    private readonly MyController controller = new MyController();
    
        [TestFixtureSetUp]
        public void Init()
        {
            var session = new Mock<HttpSessionStateBase>();
            session.Setup(s => s.SessionID).Returns(Guid.NewGuid().ToString());
            var request = new Mock<HttpRequestBase>();
            var response = new Mock<HttpResponseBase>();
            var server = new Mock<HttpServerUtilityBase>();
            // Not working - IsAjaxRequest() is static extension method and cannot be mocked
            // request.Setup(x => x.IsAjaxRequest()).Returns(true /* or false */);
            // use this
            request.SetupGet(x => x.Headers).Returns(
                new System.Net.WebHeaderCollection
                {
                    {"X-Requested-With", "XMLHttpRequest"}
                });
    
            var context = new Mock<HttpContextBase>();
            //context
            context.Setup(ctx => ctx.Request).Returns(request.Object);
            context.Setup(ctx => ctx.Response).Returns(response.Object);
            context.Setup(ctx => ctx.Session).Returns(session.Object);
            context.Setup(ctx => ctx.Server).Returns(server.Object);
            context.SetupGet(x => x.Request).Returns(request.Object);
            context.SetupGet(p => p.Request.Url).Returns(new Uri("http://www.mytesturl.com"));
            var queryString = new NameValueCollection { { "code", "codeValue" } };
            context.SetupGet(r => r.Request.QueryString).Returns(queryString);
    
            controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
        }
    

    详情请见http://weblogs.asp.net/gunnarpeipman/using-moq-to-mock-asp-net-mvc-httpcontextbase

相关问题