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

我正在使用shanselmann的MvcMockHelper类来模拟一些使用Moq的HttpContext的东西,但我有问题是能够分配的东西,我的模拟会话对象在我的MVC控制器,然后能够读取我的unit testing相同的值来validation目的。

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

我从Scott Hanselman的MVCMockHelper开始,添加了一个小类,并进行了如下所示的修改,以允许控制器正常使用Session,unit testingvalidation由控制器设置的值。

/// <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"]); 

这不是完美的,但它足够testing。

使用Moq 3.0.308.2这里是我的unit testing中我的账户控制器设置的一个例子:

  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 (); 

我做了一个比@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); } } 

我刚刚find了一个很好的例子,说明Oxite团队如何伪装他们的HttpSessionState并在该假冒中维护一个SessionStateItemCollection集合。 这应该和我的情况一样。

编辑:

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

我认为你可以设定一个具体值的模拟期望,它应该返回任何东西。 嘲笑不是作为实际的假货,而是你可以断言的行为。

这听起来像你实际上正在寻找一个适配器,你可以环绕会话,你可以在testing期间提供不同的实现,并在运行时它将返回HttpContext会话项目?

这有道理吗?

谢谢你,@RonnBlack你的解决scheme! 在我的情况下,我不断得到这个exception,因为Session.SessionID为空:

 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