2009-02-07 2 views
40

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

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

ответ

58

Я начал с Скотта Хансельмана MVCMockHelper, добавил небольшой класс и произвел следующие изменения, чтобы позволить контроллеру использовать сессию как обычно, и модульный тест для проверки значений, установленных контроллером.

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

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

+1

Спасибо за этот образец, это было очень полезно. Я немного изменил ваш MockHttpSession, чтобы возвращать null, а не бросать исключение, когда ключ не существует в словаре, чтобы более точно имитировать объект HttpSession. Просто совет для других потребителей. – DavidWhitney

+0

То, что мне нужно. +1 – fre0n

+0

Я пытался это сделать без возможности ссылки на насмешливую структуру, и ваш MockHttpSession - лучший пример, который я нашел до сих пор. Я обнаружил, что изменение getter как такового получило {return _sessionStorage.ContainsKey (имя)? _sessionStorage [имя]: null; } позволит тестировать код, который записывается как - if (sessionProperty ["some key"] == null) {} – 2012-04-02 18:19:57

0

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

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

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

2

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

EDIT:

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

+1

в интересах других, которые могут найти этот вопрос на основе поиска, не могли бы вы разместить ссылку на информацию, которую вы нашли, которая отвечает на вопрос. –

+0

+1 ссылка для получения более подробной информации. –

+0

Я думаю, что он говорит об этом классе: http: //oxite.codeplex.com/sourcecontrol/changeset/view/33871? projectName = oxite # 388065 – andrecarlucci

31

Использование 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(); 
+0

Отличный ответ! Это было именно то, что мне нужно было для тестирования тестовых данных из моей сессии. – Rob

+0

Работал отлично. Спасибо!! –

3

Я сделал немного более сложный, чем Mock ответ разместил @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

обновлен, чтобы отразить ** Исправление свойств Keys ** - просто нужна еще одна коллекция для ключей отслеживания - за [ это связанное сообщение SO] (http://stackoverflow.com/a/13277399/175679) – SliverNinja

0

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

Смежные вопросы