Как вы издеваетесь над коллекцией объектов сессии, используя Moq - PullRequest
43 голосов
/ 07 февраля 2009

Я использую класс shanselmann MvcMockHelper , чтобы смоделировать некоторые вещи HttpContext, используя Moq, но у меня возникла проблема с возможностью назначить что-то для моего смоделированного объекта сеанса в контроллере MVC, а затем возможность прочитать это же значение в моем модульном тесте для целей проверки.

Мой вопрос заключается в том, как назначить коллекцию хранилищ для смоделированного объекта сеанса, чтобы позволить коду, например session ["UserName"] = "foo", сохранить значение "foo" и сделать его доступным в модульном тесте.

Ответы [ 7 ]

63 голосов
/ 27 марта 2009

Я начал с 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"]);

Это не идеально, но достаточно для тестирования.

34 голосов
/ 13 марта 2009

Используя 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 ();
3 голосов
/ 25 февраля 2011

Я сделал немного более сложный макет, чем ответ, отправленный @ 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);
    }
}
2 голосов
/ 03 марта 2009

Я только что нашел хороший пример того, как команда Oxite подделывает свой HttpSessionState и поддерживает коллекцию SessionStateItemCollection в этом фальшивом. Это должно работать так же хорошо, как moq в моем случае.

EDIT:

URL для этого примера: http://oxite.codeplex.com/sourcecontrol/changeset/view/33871?projectName=oxite#388065

0 голосов
/ 25 апреля 2019

Просто для Session проще создать объект Session в родительском классе и использовать его вот так

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

и в unitTest

            var session = new  Dictionary<string, object>();
        var moq = new Moq.Mock<IDalBl>();
        moq.Setup(d => d.Session).Returns(session);
0 голосов
/ 06 августа 2015

Спасибо, @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()

Чтобы решить эту проблему, я реализую код @ RonnBlack таким образом, используя Moq Mock<HttpSessionStateBase> вместо его MockHttpSession:

    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

0 голосов
/ 11 февраля 2009

Я думаю, что вы можете установить ожидание для макета с определенным значением, которое оно должно вернуть что угодно. Насмешки не используются как настоящие подделки, а скорее вещи, на которые вы можете отстаивать свое поведение.

Похоже, вы на самом деле ищете адаптер, который можно обернуть вокруг сеанса, который вы можете предоставить другой реализации во время тестов, и во время выполнения он будет возвращать элементы сеанса HttpContext?

Имеет ли это смысл?

...