2010-03-23 43 views
28

Sto provando a scrivere un test unitario per il mio controller per verificare se una vista è stata restituita correttamente, ma questo controller ha un controller di base che accede a HttpContext.Current.Session. Ogni volta che creo una nuova istanza del mio controller, richiama il costruttore di basecontroller e il test fallisce con un'eccezione di puntatore nullo su HttpContext.Current.Session. Ecco il codice:Test controller unità ASP.NET MVC con HttpContext

public class BaseController : Controller 
{  
    protected BaseController() 
    { 
     ViewData["UserID"] = HttpContext.Current.Session["UserID"]; 
    } 
} 

public class IndexController : BaseController 
{ 
    public ActionResult Index() 
    { 
     return View("Index.aspx"); 
    } 
} 

    [TestMethod] 
    public void Retrieve_IndexTest() 
    { 
     // Arrange 
     const string expectedViewName = "Index"; 

     IndexController controller = new IndexController(); 

     // Act 
     var result = controller.Index() as ViewResult; 

     // Assert 
     Assert.IsNotNull(result, "Should have returned a ViewResult"); 
     Assert.AreEqual(expectedViewName, result.ViewName, "View name should have been {0}", expectedViewName); 
    } 

Delle idee su come prendere in giro (con MOQ) la sessione a cui si accede al controller di base quindi il test nel controller discendente verrà eseguito?

risposta

5

Se si utilizza Typemock, si può fare questo: codice

Isolate.WhenCalled(()=>controller.HttpContext.Current.Session["UserID"]) 
.WillReturn("your id"); 

Il test sarà simile:

[TestMethod] 
public void Retrieve_IndexTest() 
{ 
    // Arrange 
    const string expectedViewName = "Index"; 

    IndexController controller = new IndexController(); 
    Isolate.WhenCalled(()=>controller.HttpContext.Current.Session["UserID"]) 
    .WillReturn("your id"); 
    // Act 
    var result = controller.Index() as ViewResult; 

    // Assert 
    Assert.IsNotNull(result, "Should have returned a ViewResult"); 
    Assert.AreEqual(expectedViewName, result.ViewName, "View name should have been {0}", expectedViewName); 
} 
3

Probabilmente dovresti usare un ActionFilter invece di una classe base per questo genere di cose

[UserIdBind] 
public class IndexController : Controller 
{ 
    public ActionResult Index() 
    { 
     return View("Index.aspx"); 
    } 
} 
+0

Se uso il metodo del filtro azione che avrebbero allora per decorare ogni azione con questo attributo e con circa 400 azioni che ISN 'fattibile – amurra

+0

@ user299592: No, non lo faresti. Puoi applicarlo a livello di classe (come nell'esempio) e lo applicherà a ogni azione in quella classe. Se pensi che sarà più impegnativo che prendere in giro un contesto per ogni test su ogni azione (pochi o nessuno dei quali effettivamente usa il campo che stai impostando nel costruttore), abbastanza equo. – pdr

+0

Il tuo corretto non l'ho visto a livello di classe, ma non sarebbe ancora chiamato il filtro di azione durante l'istanziazione dell'oggetto controller? – amurra

61

A meno che non si utilizzi Typemock o Moles, you can't.

In ASP.NET MVC non si suppone che utilizzi HttpContext.Current. Modifica la classe base per utilizzare ControllerBase.ControllerContext - ha una proprietà HttpContext che espone la classe controllabile HttpContextBase.

Ecco un esempio di come è possibile utilizzare Moq per impostare una HttpContextBase Mock:

var httpCtxStub = new Mock<HttpContextBase>(); 

var controllerCtx = new ControllerContext(); 
controllerCtx.HttpContext = httpCtxStub.Object; 

sut.ControllerContext = controllerCtx; 

// Exercise and verify the sut 

dove sut rappresenta il sistema in prova (SUT), vale a dire il controller che si desidera testare.

+0

puoi indicarmi la direzione di come deridere questo con Moles, per favore? – BritishDeveloper

+0

Decisamente la strada da percorrere; Microsoft ha aggiunto HttpContextBase per questo motivo preciso. Ora, se solo Cache e FormsAuthentication non fossero sigillati/statici ... –

3

Snippet:

var request = new SimpleWorkerRequest("/dummy", @"c:\inetpub\wwwroot\dummy", "dummy.html", null, new StringWriter()); 
var context = new HttpContext(request); 
SessionStateUtility.AddHttpSessionStateToContext(context, new TestSession()); 
HttpContext.Current = context; 

Attuazione Pass per assistere():

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Web.SessionState; 

namespace m1k4.Framework.Test 
{ 
    public class TestSession : IHttpSessionState 
    { 
     private Dictionary<string, object> state = new Dictionary<string, object>(); 

     #region IHttpSessionState Members 

     public void Abandon() 
     { 
      throw new NotImplementedException(); 
     } 

     public void Add(string name, object value) 
     { 
      this.state.Add(name, value); 
     } 

     public void Clear() 
     { 
      throw new NotImplementedException(); 
     } 

     public int CodePage 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
      set 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public System.Web.HttpCookieMode CookieMode 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public void CopyTo(Array array, int index) 
     { 
      throw new NotImplementedException(); 
     } 

     public int Count 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public System.Collections.IEnumerator GetEnumerator() 
     { 
      throw new NotImplementedException(); 
     } 

     public bool IsCookieless 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public bool IsNewSession 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public bool IsReadOnly 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public bool IsSynchronized 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public System.Collections.Specialized.NameObjectCollectionBase.KeysCollection Keys 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public int LCID 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
      set 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public SessionStateMode Mode 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public void Remove(string name) 
     { 
      this.state.Remove(name); 
     } 

     public void RemoveAll() 
     { 
      this.state = new Dictionary<string, object>(); 
     } 

     public void RemoveAt(int index) 
     { 
      throw new NotImplementedException(); 
     } 

     public string SessionID 
     { 
      get 
      { 
       return "Test Session"; 
      } 
     } 

     public System.Web.HttpStaticObjectsCollection StaticObjects 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public object SyncRoot 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public int Timeout 
     { 
      get 
      { 
       return 10; 
      } 
      set 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public object this[int index] 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
      set 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public object this[string name] 
     { 
      get 
      { 
       return this.state[name]; 
      } 
      set 
      { 
       this.state[name] = value; 
      } 
     } 

     #endregion 
    } 
} 
+1

Se si utilizza Rhino.Mocks, è possibile eseguire semplicemente 'SessionStateUtility.AddHttpSessionStateToContext (httpContext, MockRepository.GenerateStub ());' invece di implementare TestSession da soli. –

Problemi correlati