Tester les contrôleurs web api avec OWIN
Lors des Techdays 2014, j’ai animé une session avec Simon sur les nouveautés d’ASP.NET MVC 5 et Web API 2. Lors de cette session, nous avons notamment parlé d’OWIN, l’une des principales nouveautés de la plateforme Web Microsoft, et aussi certainement la plus sympa (pour moi en tout cas) !
Introduction au développement de middleware OWIN
Je vous ai déjà parlé d’OWIN (Open Web Interface for .NET) sur ce blog. Pour faire court, il s’agit d’un ensemble de spécifications établies par Microsoft qui définissent comment une application web s’exécute sur un serveur web, et qui permettent notamment de s’abstraire de l’host qui exécutera l’application au final. Vous retrouverez plus d’informations à propos d’OWIN sur cette page.
OWIN se base sur le développement de middleware qui sont chaînés entre eux et qui permettent d’ajouter des fonctionnalités à une application. On les utilise notamment pour mettre en place de l’authentification externe avec des fournisseurs d’identité tels que Facebook, Microsoft, Google ou encore Azure Active Directory. Ils peuvent aussi être de simples composants techniques qui permettent à une application d’effectuer un traitement particulier, par exemple logguer les requête http. Chaque middleware OWIN est responsable de sa fonctionnalité métier.
Il est très simple de créer un middleware OWIN : il s’agit d’une classe qui dérive de OwinMiddleware, comme par exemple la classe LogsMiddleware ci-dessous :
public class LogMiddleware : OwinMiddleware { public LogMiddleware(OwinMiddleware next) : base(next) { } public async override Task Invoke(IOwinContext context) { Debug.WriteLine("Request begins: {0} {1}", context.Request.Method, context.Request.Uri); await Next.Invoke(context); Debug.WriteLine("Request ends : {0} {1}", context.Request.Method, context.Request.Uri); } }
Comme vous pouvez le constater, le middleware OWIN prend dans son constructeur le prochain middleware à exécuter et vous propose de définir une méthode Invoke qui prend en paramètre le contexte OWIN courant. Dans l’exemple ci-dessus, on logue simplement le début et la fin de la requête http, et on invoque le prochain middleware OWIN à exécuter (selon la configuration, ce pourra être un provider d’authentification, par exemple).
Maintenant que le middleware est écrit, il est nécessaire d’indiquer à notre application qu’elle doit l’utiliser. Et c’est là toute la puissance d’OWIN, puisque ce middleware de logs est compatible avec n’importe quelle technologie supportant OWIN, en l’occurrence ASP.NET Web Forms, MVC, Web API ou encore Signal-R.
Pour déclarer l’utilisation d’un middleware OWIN dans une application, on utilise une classe de Startup OWIN. Visual Studio nous fournit de base un template permettant de créer la structure de cette classe :
using Microsoft.Owin; using Owin; [assembly: OwinStartupAttribute(typeof(EbookManager.Web.Startup))] namespace EbookManager.Web { public partial class Startup { public void Configuration(IAppBuilder app) { } } }
Comme vous pouvez le constater la définition de la classe est ultra simple. Elle ne contient qu’une méthode Configure qui prend en paramètre un objet de type IAppBuilder qui représente votre application OWIN. Pour exposer cette classe de configuration à OWIN, il faut utiliser l’attribut OwinStartupAttribute au niveau assembly, comme c’est fait au dessus de la définition du namespace.
Il est à présent nécessaire d’enregistrer le middleware de logs dans l’application OWIN, de la manière suivante :
public void Configuration(IAppBuilder app) { app.Use(typeof(LogMiddleware)); }
Et voilà, il n’y a rien de plus à faire pour que cela fonctionne !
Self-Hosting avec OWIN
OWIN, ce n’est pas que des middlewares, c’est aussi une abstraction de l’host qui va exécuter votre application.
Microsoft en propose 3 actuellement.
- Le serveur IIS, que vous connaissez très certainement et qui permet d’exécuter des applications ASP.NET (Web Forms, MVC, Web API) ou SignalR.
- HttpListener qui permet d’hoster une web api ou un hub signalR dans une application .NET (service windows, application console…)
- Unit test host, qui nous intéresse plus particulièrement aujourd’hui et qui permet d’exécuter des tests unitaires d’api web en s’affranchissant du déploiement sur un serveur !
Utiliser OWIN pour tester des Web APIs
Microsoft propose un package NuGet pour récupérer un serveur de test OWIN : Microsoft.Owin.Testing.
Pour faire fonctionner ce serveur de test, il est nécessaire de lui fournir la configuration OWIN qui va bien pour l’application à tester (l’équivalent de la Startup class OWIN, mais sans la déclaration via l’attribut OwinStartupAttribute) :
class OwinTestConf { public void Configuration(IAppBuilder app) { app.Use(typeof (LogMiddleware)); HttpConfiguration config = new HttpConfiguration(); config.Services.Replace(typeof(IAssembliesResolver), new TestWebApiResolver()); config.MapHttpAttributeRoutes(); app.UseWebApi(config); } }
Ici, vous pouvez constater que l’on enregistre également le middleware de logs écrit plus haut dans l’article, puis que l’on fait la configuration de Web API comme on le ferait dans n’importe quelle application. La seule petite différence réside dans l’utilisation d’un assembly resolver personnalisé, puisque les contrôleurs Web API ne sont pas définis dans la même assembly que les tests.
Vous pouvez ensuite récupérer un serveur de test OWIN de la manière suivante et le requêter à l’aide d’un simple HttpClient :
using (var server = TestServer.Create<OwinTestConf>()) { using (var client = new HttpClient(server.Handler)) { var response = await client.GetAsync("http://testserver/api/values"); var result = await response.Content.ReadAsAsync<List<string>>(); Assert.IsTrue(result.Any()); } }
Il est important de noter qu’ici, on passe un handler fourni par le serveur de test OWIN à l’HttpClient, ce qui permet de by-passer l’utilisation du protocole http et de faire les appels Web API “in process”.
Conclusion
Comme vous avez pu le voir dans ce post, OWIN est une technologie ultra simple à mettre en place, et qui est capable de vous rendre de grands services, notamment ici sur la problématique des tests ! Il ne faut pas hésiter à en abuser
Dans un prochain billet, je vous expliquerai comment tester une Web API qui nécessite d’être un utilisateur authentifié.
En attendant, vous pourrez trouver des exemples de middleware OWIN et de leur utilisation dans le sample que Simon et moi-même avons développé pour les Techdays et qui est dispo sur GitHub.
A+
Julien
Commentaires