2009-02-07 7 views
40

Sto usando la classe shanselmann's MvcMockHelper per prendere in giro alcune cose di HttpContext usando Moq ma il problema che sto avendo è di poter assegnare qualcosa al mio oggetto di sessione deriso nel mio controller MVC e quindi essere in grado di leggere lo stesso valore nel mio test unitario a scopo di verifica.Come si prende in giro la raccolta di oggetti di sessione usando Moq

La mia domanda è: come si assegna una raccolta di memoria all'oggetto sessione mocked per consentire a codice come session ["UserName"] = "foo" di mantenere il valore "foo" e farlo essere disponibile nel test dell'unità .

risposta

58

Ho iniziato con lo MVCMockHelper di Scott Hanselman, ho aggiunto una piccola classe e ho apportato le modifiche mostrate di seguito per consentire al controller di utilizzare normalmente Session e il test dell'unità per verificare i valori impostati dal controller.

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

Non è perfetto ma funziona abbastanza per il test.

+1

Grazie per questo esempio, è stato molto utile. Ho modificato leggermente il tuo MockHttpSession per restituire null piuttosto che generare un'eccezione quando la chiave non esiste nel dizionario per imitare più da vicino l'oggetto HttpSession. Solo un consiglio per altri consumatori. – DavidWhitney

+0

Esattamente quello di cui avevo bisogno. +1 – fre0n

+0

Ho cercato di fare questo w/o attualmente in grado di fare riferimento a un framework di simulazione, e il tuo MockHttpSession è il miglior esempio che ho trovato finora. Ho scoperto che cambiando il getter in quanto tale ottieni {return _sessionStorage.ContainsKey (name)? _sessionStorage [nome]: null; } consentirà il test del codice che viene scritto come - if (sessionProperty ["some key"] == null) {} –

0

Penso che sia possibile impostare un'aspettativa sul mock con un valore specifico che dovrebbe restituire qualsiasi cosa. I mock non vengono usati come falsi effettivi, ma piuttosto come cose su cui puoi affermare il comportamento.

Sembra che si stia effettivamente cercando un adattatore che è possibile avvolgere attorno alla sessione per poter fornire un'implementazione diversa durante i test e durante il runtime restituire gli elementi di sessione HttpContext?

Ha senso?

2

Ho appena trovato un bell'esempio di come il team di Oxite falsi il proprio HttpSessionState e mantenga una collezione SessionStateItemCollection all'interno di tale falso. Questo dovrebbe funzionare come un moq nel mio caso.

EDIT:

URL per questo esempio è http://oxite.codeplex.com/sourcecontrol/changeset/view/33871?projectName=oxite#388065

+1

per il beneficio di altri che trovano questa domanda tramite le ricerche, potresti inserire un link alle informazioni trovate che rispondono alla domanda. –

+0

+1 a un link per maggiori informazioni su questo. –

+0

Penso che stia parlando di questa classe: http: //oxite.codeplex.com/sourcecontrol/changeset/view/33871? projectName = oxite # 388065 – andrecarlucci

31

Utilizzando Moq 3.0.308.2 qui è un esempio del mio conto di configurazione del controller nel mio test di unità:

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

quindi entro il controller metodo il seguente dovrebbe restituire "Hello World"

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

Ottima risposta! Questo era esattamente ciò di cui avevo bisogno per testare i dati dell'unità dalla mia sessione – Rob

+0

Perfettamente funzionante. Grazie!! –

3

Ho fatto un Mock un po 'più elaborato di risposta inviato da @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

aggiornato per riflettere ** Keys ** fix di proprietà - serve solo un'altra raccolta per le chiavi di tracciamento - per [ questo post SO correlato] (http://stackoverflow.com/a/13277399/175679) – SliverNinja

0

Grazie, @RonnBlack per la vostra soluzione! Nel mio caso, ho continuato a ottenere questa eccezione perché Session.SessionID era nullo:

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

Per risolvere questo problema implemento @ codice di RonnBlack questo modo utilizzando il Moq Mock<HttpSessionStateBase> al posto del suo 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); 
    } 

Per dettagli, vedi http://weblogs.asp.net/gunnarpeipman/using-moq-to-mock-asp-net-mvc-httpcontextbase