HttpContextWrapper est-il tout ce que...utile?
J'ai passé par le processus de nettoyage de notre code de contrôleur pour rendre chaque action testable. D'une manière générale, cela n'a pas été trop difficile-lorsque nous avons la possibilité d'utiliser un objet fixe, comme par exemple FormsAuthentication, nous introduisons généralement une forme de wrapper le cas échéant et sommes sur notre chemin joyeux.
Pour des raisons pas particulièrement germaine à cette conversation, quand il est venu à traiter avec L'utilisation de HttpContext, nous avons décidé d'utiliser le nouvellement créé HttpContextWrapper classe plutôt que d'inventer quelque chose homegrown. Une chose que nous avons introduite était la possibilité d'échanger dans un HttpContextWrapper (comme par exemple, pour les tests unitaires). Cela a été entièrement inspiré par la façon dont Oren Eini gère les tests unitaires avec DateTimes (voir l'article, un modèle que nous utilisons également)
public static class FooHttpContext
{
public static Func<HttpContextWrapper> Current = ()
=> new HttpContextWrapper(HttpContext.Current);
public static void Reset()
{
Current = () => new HttpContextWrapper(HttpContext.Current);
}
}
Rien de particulier. Et cela fonctionne très bien dans notre code de contrôleur. Le kicker est venu quand nous allons écrire des tests unitaires. Nous utilisons Moq comme notre cadre moqueur, mais hélas
var context = new Mock<HttpContextWrapper>()
Se casse puisque HttpContextWrapper n'a pas de ctor sans paramètre. Et que prend-il comme paramètre ctor? Un objet HttpContext. Donc, je me trouve dans une prise 22.
J'utilise la manière prescrite pour découpler HttpContext-mais je ne peux pas me moquer d'une valeur car l'objet HttpContext original était scellé et donc difficile à tester. Je peux mapper HttpContextBase, qui dérivent tous les deux de-mais cela ne me donne pas vraiment ce que je recherche. Est ce que je manque juste le point quelque part en ce qui concerne HttpContextWrapper?
Modifier afin de clarifier l'intention
Nous avons trouvé des moyens de résoudre le problème - mais je suppose que la question ultime que nous nous éloignons est quelle valeur HttpContextWrapper apporte à la table? Je ne doute pas que quelqu'un ait eu un a-ha! un moment avec ça, mais ça ne vient pas à moi. La plupart des messages que je vois ici en discutent en termes de testabilité-mais ma propre expérience m'a amené à croire que cela n'apportait pas grand-chose cadre. Sauf si nous le faisons mal. (Tout à fait possible).
2 réponses
Vous devriez utiliser l'abstract HttpContextBase
qui est beaucoup plus facile à moquer au lieu de HttpContextWrapper
.
public static Func<HttpContextBase> Current =
() => new HttpContextWrapper(HttpContext.Current);
Et dans votre test unitaire:
SomeClass.Current = MockHttpContextBase(); // Sorry I don't know the syntax for Moq
Ce billet de blog l'explique assez bien:
Http://splinter.com.au/httpcontext-vs-httpcontextbase-vs-httpcontext
Le fait est que' vintage ' HttpContext n'implémente pas HttpContextBase, et n'est pas virtuel, et ne peut donc pas être moqué. HttpContextBase a été introduit dans 3.5 comme une alternative moquable. Mais il y a toujours le problème que HttpContext vintage n'implémente pas HttpContextBase.
Donc HttpContextWrapper est une classe wrapper pratique (ou 'kludge') qui implémente HttpContextBase, et peut être utilisé lors de l'injection d'un 'vrai' HttpContext en utilisant IOC, généralement avec une méthode factory comme ceci: () => new HttpContextWrapper(HttpContext.Current)