asp.net-mvc 你如何使用 Moq 模拟会话对象集合

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/524457/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-07 23:19:27  来源:igfitidea点击:

How do you mock the session object collection using Moq

asp.net-mvcunit-testingtddmoq

提问by rayray2030

I am using shanselmann's MvcMockHelperclass to mock up some HttpContext stuff using Moq but the issue I am having is being able to assign something to my mocked session object in my MVC controller and then being able to read that same value in my unit test for verification purposes.

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

My question is how do you assign a storage collection to the mocked session object to allow code such as session["UserName"] = "foo" to retain the "foo" value and have it be available in the unit test.

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

回答by RonnBlack

I started with Scott Hanselman's MVCMockHelper, added a small class and made the modifications shown below to allow the controller to use Session normally and the unit test to verify the values that were set by the controller.

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

It's not perfect but it works enough for testing.

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

回答by Todd Smith

Using Moq 3.0.308.2 here is an example of my account controller setup in my unit test:

在我的单元测试中使用 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;
    }

then within your controller method the following should return "Hello World"

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

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

回答by Chris Marisic

I've made a slightly more elaborate Mock than the answer posted by @RonnBlack

我做了一个比@RonnBlack 发布的答案更复杂的 Mock

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

回答by rayray2030

I just found a nice example of how the Oxite team fakes their HttpSessionState and maintains a SessionStateItemCollection collection within that fake. This should work just as well as a moq in my case.

我刚刚找到了一个很好的例子,说明 Oxite 团队如何伪造他们的 HttpSessionState 并在该伪造中维护一个 SessionStateItemCollection 集合。在我的情况下,这应该和最小起订量一样有效。

EDIT:

编辑:

URL for this example is http://oxite.codeplex.com/sourcecontrol/changeset/view/33871?projectName=oxite#388065

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

回答by Sean Chambers

I think you can set an expectation on the mock with a specific value it should return whatever. Mocks are not used as actual fakes but rather things that you can assert behavior on.

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

It sounds like you are actually looking for an adapter that you can wrap around the session that you can supply a different implementation during tests and during runtime it would return HttpContext Session items?

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

Does this make sense?

这有意义吗?

回答by user8128167

Thank you, @RonnBlack for your solution! In my case, I kept getting this exception because Session.SessionID was null:

谢谢@RonnBlack 的解决方案!就我而言,我一直收到此异常,因为 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()

To solve this problem I implement @RonnBlack's code this way using the Moq Mock<HttpSessionStateBase>instead of his MockHttpSession:

为了解决这个问题,我使用 MoqMock<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);
    }

For details, please see http://weblogs.asp.net/gunnarpeipman/using-moq-to-mock-asp-net-mvc-httpcontextbase

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

回答by Ali Humayun

Just for Session easier way is to create Session object in parent class and use it like this

仅用于 Session 更简单的方法是在父类中创建 Session 对象并像这样使用它

    public class DalBl : IDalBl
{
    public dynamic Session
    {
        get { return HttpContext.Current.Session; }
    }
}

and in unitTest

并在单元测试中

            var session = new  Dictionary<string, object>();
        var moq = new Moq.Mock<IDalBl>();
        moq.Setup(d => d.Session).Returns(session);