2010-03-09 14 views
24

Ho eseguito il processo di pulizia del codice del controller per rendere ogni azione come verificabile. In generale, questo non è stato troppo difficile - dove abbiamo l'opportunità di usare un oggetto fisso, come ad esempio FormsAuthentication, generalmente introduciamo qualche forma di wrapper come appropriato e ci stiamo divertendo.HttpContextWrapper è tutto questo .... utile?

Per motivi non particolarmente pertinenti a questa conversazione, quando si trattava di utilizzare HttpContext, abbiamo deciso di utilizzare la classe HttpContextWrapper appena creata piuttosto che inventare qualcosa di homegrown. Una cosa che abbiamo introdotto è stata la possibilità di scambiare un HttpContextWrapper (come dire, per il test delle unità). Questo è stato totalmente ispirato dal modo in cui Oren Eini gestisce unit testing con DateTimes (see article, un modello che usiamo anche)

public static class FooHttpContext 
{ 
    public static Func<HttpContextWrapper> Current =() 
     => new HttpContextWrapper(HttpContext.Current); 

    public static void Reset() 
    { 
     Current =() => new HttpContextWrapper(HttpContext.Current); 
    } 
} 

Niente di particolarmente fantasia. E funziona bene nel nostro codice controller. Il kicker è arrivato quando andiamo a scrivere i test unitari. Stiamo usando Moq come il nostro quadro di scherno, ma ahimè

var context = new Mock<HttpContextWrapper>() 

pause dal HttpContextWrapper non dispone di un ctor senza parametri. E cosa ci vuole come parametro Ctor? Un oggetto HttpContext. Così mi ritrovo in una presa 22.

Sto usando il modo prescritto per disaccoppiare HttpContext - ma non posso prendere in giro un valore perché l'oggetto HttpContext originale era sigillato e quindi difficile da testare. Posso mappare HttpContextBase, da cui entrambi derivano - ma questo non mi dà davvero quello che sto cercando. Mi manca solo il punto da qualche parte per quanto riguarda HttpContextWrapper?

Modifica per chiarire l'intento

Abbiamo trovato modi per risolvere il problema - ma immagino l'ultima domanda che stiamo camminando via con è che valore HttpContextWrapper porta in tavola? Non dubito che qualcuno abbia totalmente un a-ha! momento con esso, ma semplicemente non viene a me. La maggior parte dei post che vedo qui ne discute in termini di testabilità, ma la mia esperienza mi ha portato a credere che non abbia portato molto in questo contesto. A meno che non stiamo sbagliando. (Completamente possibile).

risposta

30

Si dovrebbe usare l'abstract HttpContextBase che è molto più facile da simulare al posto di HttpContextWrapper.

public static Func<HttpContextBase> Current = 
    () => new HttpContextWrapper(HttpContext.Current); 

E nel tuo test di unità:

SomeClass.Current = MockHttpContextBase(); // Sorry I don't know the syntax for Moq 
+0

You' Ho assolutamente ragione - e ciò che alla fine abbiamo fatto ... ma la mia domanda in più credo fosse su ciò che Wrapper porta davvero sul tavolo - lasciami modificare per chiarezza. – bakasan

+1

'HttpContextWrapper' non è pensato per essere un'astrazione ma un'implementazione concreta di' HttpContextBase'. Immagino che il suo valore sia che nasconde alcuni metodi statici e interni di 'HttpContext'. –

+7

'HttpContextWrapper' implementa il' HttpContextBase' (mockable) inoltrando le chiamate al 'HttpContext' di ASP.NET-vintage. È una verruca per aggirare il fatto che 'HttpContext' non è mockable. –

31

Questo post del blog spiega abbastanza bene:

http://splinter.com.au/httpcontext-vs-httpcontextbase-vs-httpcontext

Il punto è che 'vintage' HttpContext non implementa HttpContextBase, e non è virtuale, e quindi non può essere deriso. HttpContextBase è stato introdotto in 3.5 come alternativa mockable. Ma c'è ancora il problema che HttpContext vintage non implementa HttpContextBase.

Così HttpContextWrapper è una classe wrapper a portata di mano (o 'kludge') che non implementa HttpContextBase, e può essere utilizzato quando l'iniezione di un 'vero' HttpContext con IOC, di solito con un metodo factory come questo: () => new HttpContextWrapper(HttpContext.Current)

+4

E avresti impostato la registrazione in Unity come segue; 'var container = new UnityContainer(); container.RegisterType (nuovo InjectionConstructor (HttpContext.Current)); ' –