Azure Functions: Inyección de dependencias

Hello world,

Hace unas semanas tuve que montar la arquitectura base para un proyecto en el cual su principal premisa es que todo tendria que estar alojado en cloud, y a ser posible todo tendría que ser serverless. Para ello como no, decidimos tirar por Azure Functions. La siguiente decisión a tomar era que utilizar por debajo, teniendo en cuenta que tendríamos algo de lógica, y alguna que otra integración con servicios de terceros, decidimos montar una arquitectura n-layer orientada a dominio, pero sin llevar el DDD al extremo. Esta arquitectura nos permitiría tener las distintas piezas lo suficientemente desacopladas como para que no nos diera problemas en el futuro (la idea es que esta infraestructura siga creciendo)  y sobre todo, poder crear un buen ecosistema de testing, tanto de test unitarios como de test de integración.

Una vez decididas estas cosas, el primer punto que tuvimos que solventar, fue como resolver las distintas dependencias que generaríamos entre las distintas capas del proyecto. Evidentemente para esto lo primero que pensamos fue en la inyección de dependencias, pero claro:

¿Cómo creamos el contenedor IoC?

¿Desde dónde lo instanciamos?

¿…?

Y así surgieron un largo listado de preguntas… Así que nada, hicimos lo primero que hace cada developer cuando empieza un nuevo desarrollo, y ese desarrollo no es el típico ni habitual desarrollo al que esta acostumbrado. Lo primero que vimos es lo de siempre, en la documentación oficial, no pasamos de tener el típico ejemplo donde toda la “lógica” de la function esta en el propio método Run. Evidentemente en castellano, 0 información, así que nada, busquemos en ingles… Y voilà por fin aparece algo de información.

Lo primero que tendremos que hacer es crearnos nuestro propio contenedor IoC. Para ello nos creamos la clase ContainerBuilder, dónde inicialiazaremos nuestra colección de servicios, registraremos nuestro modulo con la colección de servicios y por último nos construiremos nuestro contenedor.

using Microsoft.Extensions.DependencyInjection;

public class ContainerBuilder: IContainerBuilder
    {
        private readonly IServiceCollection _services;
        
        public ContainerBuilder()
        {
            _services = new ServiceCollection();
        }
        
        public IContainerBuilder RegisterModule(IModule module = null)
        {
            if (module == null)
            {
                module = new Module();
            }
            module.Load(_services);
            return this;
        }
        
        public IServiceProvider Build()
        {
            var provider = _services.BuildServiceProvider();
            return provider;
        }
    }
    public interface IContainerBuilder
    {
        IContainerBuilder RegisterModule(IModule module = null);
        IServiceProvider Build();
    }

Como podemos ver, utilizaremos un contrato dónde expondremos el método para registrar el nuestro modulo de IoC (para el cual nos crearemos un tipo IModule que será el que marque el tipo de nuestro modulo), y el Build que nos construirá nuestro contenedor.

    using Microsoft.Extensions.DependencyInjection;

    public class Module: IModule
    {
        public virtual void Load(IServiceCollection services)
        {
            return;
        }
    }
    public interface IModule
    {
        void Load(IServiceCollection services);
    }

Por último, nos queda crear nuestro modulo dónde registraremos nuestras dependencias, el que simulará nuestra querida clase Startup que tenemos en nuestros proyectos de ASP.NET Core.

    public class CoreAppModule : Module
    {
        public override void Load(IServiceCollection services)
        {
            // Registro de los distintos midelwares necesarios, como por       ejemplo el que se suele utilizar para leer los archivos de configuración de nuestras aplicaciones
            var config = new ConfigurationBuilder()
                     .SetBasePath(Directory.GetCurrentDirectory())
                     .AddJsonFile("appsettings.json")
                     .Build();

            // Resolución de nuestros servicios
            services.AddTransient<IMyService, MyService>();
        }
    }

Como podemos ver en el fragmento de código, nuestra clase hereda de Module, para que podamos posteriormente resolverla desde nuestra function. Y a partir de ahí, registraremos los distintos middlewares que necesitemos, como lo haríamos en una aplicación de ASP.NET Core. Como información extra, dado que no preveemos que la resolución de nuestras dependencias se complique mas allá de tener que inyectar algún parámetro por constructor, decidimos utilizar el contenedor IoC nativo que viene en ASP.NET Core, pero si fuera necesario podríamos utilizar otro, como por ejemplo Autofact.

Hasta aquí ya tenemos montado todo lo necesario para poder crear nuestro contenedor de dependencias para utilizarlo desde nuestras functions, pero queda lo más importante…

¿Cómo lo instanciamos? y ¿desde dónde?

Evidentemente en las functions no tenemos una clase Startup, ni nada parecido, con lo que tendremos que hacerlo desde la propia function. Para ello nos declararemos nuestro container, y lo inicializaremos. Una vez hecho esto, ya podremos utilizarlo desde nuestra function, y sobre todo, todos nuestros servicios, agentes, repositorios, etc, a los que inyectemos cualquier cosa que tengamos registrada en nuestro container, esta estará ya resuelta.

private static readonly IServiceProvider _container = new ContainerBuilder()
                                                 .RegisterModule(new CoreAppModule())
                                                 .Build();

Otro detallito antes de terminar, para poder resolver cualquier cosa que hayamos declarado en nuestro contenedor desde la propia function, utilizaremos el patrón Service Locator. OJO el uso de este patrón SOLO será necesario en nuestra function, que como ya hemos visto no tendrá constructor. Pero en el resto de capas, podremos inyectar nuestros servicios por constructor sin ningún tipo de problema.

var myService = _container.GetService<IMyService>();

Os dejo el repo que utilizamos de guía para hacer el desarrollo.

Espero que os haya servido de ayuda. Cualquier cosa no dudéis en dejarlo en los comentarios.

Y con este post despido el 2018, no he escrito tanto como me gustaría pero al final no hay tiempo para todo. Aprovecho para desearos unas felices fiestas, y sobre todo una feliz salida y entrada de año 2019!

Un abrazo.

Anuncios
Azure Functions: Inyección de dependencias

[Tips] Reactjs en Azure App Service

Hello world,

Recientemente con mi equipo nos hemos encontrado diversos problemas a la hora de desplegar la aplicación que estamos desarrollando. El desarrollo se está realizando en reactjs, y el despliegue en un Azure App Service. Realmente, más que a la hora de desplegar, los problemas surgieron una vez desplegada la App, ya que varias cosillas no funcionaban como esperábamos.

Pongo en contexto:

  • Decidimos utilizar para el desarrollo del frontal reactjs, dado el expertise del equipo, y sobre todo por las buenas experiencias en proyectos anteriores con esta tecnología. Para paquetizarla, utilizamos la última versión de webpack. Hasta aquí todo correcto… npm run build y ¡a correr!
  • Por requerimiento de cliente, tendríamos que desplegar esta app en un Azure App Service, basado en windows. Esto sería perfecto, que ya hemos desplegado mil apps en Azure con lo que no tendríamos que tener problemas, dicho sea de paso, nada más lejos de la realidad…

Una vez puestos en contexto, empezamos con la explicación de los problemas y las soluciones que aplicamos. Principalmente fueron 2 los problemas:

  1. Los archivos estáticos para las traducciones.
  2. El enrutado de las distintas pantallas.

El primer problema surgió cuando desplegamos nuestra app por primera vez. Sin motivo aparente, ¡las traducciones de los literales no salían! Abrimos la consola de depuración del google chrome, y vemos que la web no es capaz de resolver los archivos de recursos donde tenemos las traducciones, un maravilloso “404 Not Found“. A modo informativo, utilizamos la librería i18next con una configuración de backend para que coja las traducciones de unos archivos json, los cuales no están embebidos en el “macro-buddle” que genera la build. Utilizamos esta estrategia de archivos de recursos porque nos facilita tanto a nosotros como al cliente la modificación de las distintas traducciones, ya que  no tendremos que volver a generar la build y desplegar.

Archivos estáticos

El problema de las traducciones, venía dado porque nuestra Azure Web App no devolvía los archivos estáticos que le estábamos solicitando desde cliente. Realmente esto es correcto, ya que el propio IIS por seguridad capa todo tipo de peticiones a ficheros que no sean de acceso permitido. Estos ficheros se pueden especificar desde la propia configuración del Azure App Service, como se muestra en la siguiente imagen:

tempsnip
Pantalla configuración Azure App Service

Pero evidentemente, ahí no estaba configurado (por defecto) que nos devolviera nuestros archivos de traducciones, ya que esta función tampoco está pensada para esto, sino mas bien para definir el punto/s de entrada de mi web. He de reconocer, que en un primer momento, y con el tiempo de la entrega del sprint encima, se nos pasó por la cabeza poner esos archivos en la configuración de documentos predeterminados, pero realmente no vimos que esta fuera la aproximación correcta… Así que seguimos pensando y llegamos a la conclusión de que nuestra app realmente estaba corriendo en un IIS, y era este IIS el que estaba capando las peticiones sobre estos archivos, con lo que quizá metiendo un web.config donde le especificáramos al IIS que SI sirviera los archivos con extensión .json, fuera suficiente para que esto funcionase. Así que nos creamos un “mini” web.config con la siguiente configuración:

    <handlers>
      <clear />
      <add name=”StaticFile” path=”*” verb=”GET” modules=”StaticFileModule,DefaultDocumentModule,DirectoryListingModule” resourceType=”Either” requireAccess=”Read” />
    </handlers>
    <staticContent>
      <mimeMap fileExtension=”.json” mimeType=”application/json” />
    </staticContent>
 

¡Y por arte de “magia” nuestra aplicación empezó a traducir las distintas etiquetas! Como podéis ver en el XML, añadimos un handler donde especificamos que los archivos estáticos SOLO se sirvan en modo de lectura (requireAccess=”Read”), y por último, en el “mimeMap” especificamos que solo sirva los ficheros con la extensión json (fileExtension=”.json).

Enrutado

El segundo de los problemas, este ya si un poco más grave, fue con el enrutado de nuestra App, aunque en local la web funcionaba perfectamente, atacando a la URL que atacase, una vez desplegado esto no era así… Algo extraño estaba pasando, lo explico… Si la navegación era por URL, solo funcionaba si esa URL era la de la base, en cambio, si atacábamos a cualquier otra URL, nos devolvía un bonito error de que no podía resolver ese path…

The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.

En cambio, si la navegación se hacia desde la aplicación, es decir, desde un link de la app o desde la re-dirección producida por un botón, en ese caso la app SI funcionaba correctamente… Así que tocó otra vez ponerse a investigar… y la solución volvía a pasar otra vez por el web.config. 

En este caso desde el web.config teniamos que reescribir las reglas de enrutado del IIS, y para ello es tan simple como añadir las siguientes lineas:

<rewrite>
      <rules>
          <rule name=”React Routes” stopProcessing=”true”>
            <match url=”.*” />
            <conditions logicalGrouping=”MatchAll”>
              <add input=”{REQUEST_FILENAME}” matchType=”IsFile” negate=”true” />
              <add input=”{REQUEST_FILENAME}” matchType=”IsDirectory” negate=”true” />
              <add input=”{REQUEST_URI}” pattern=”^/(api)” negate=”true” />
            </conditions>
            <action type=”Rewrite” url=”/” />
          </rule>
        </rules>
    </rewrite>
 

El secreto está en decirle al IIS que te da igual lo que llegue después del host, y para ello bastará con poner un “*” en el mach (match url=”.*”).

Un ejemplo para que quede mas claro, imaginar que el host de nuestra aplicación es https://xxxxxxx/. Pues bien, todo lo que pongamos después de la última barra no será capaz de enrutarlo, es decir, si le dijéramos que queremos navegar a https://xxxxxxx/summary nos daría un error, ya que el IIS no sería capaz de interpretar esa URL, y como realmente lo que estamos sirviendo es un archivo xxxx.html en el cual hay una referencia a un js donde va TODA la configuración de la App, el IIS realmente solo actúa como un bridge para servirnos la SPA, pero toda la configuración esta dentro del js, con lo que es necesario que el IIS sirva ese xxxx.html para que el enrutado en ese caso funcione. Y de ahí el porqué de que desde los links de la App SI que funcionase la navegación, porque en ese caso, no es el IIS el que sirve la página, sino que es el propio router incluido en el js, el que lo esta haciendo.

En el siguiente link dejo mas información sobre la reescritura de URL’s:

IIS URL Rewrite

El resultado final del web.config tendría el siguiente aspecto:

webconfig
web.config

Espero que os sirva de ayuda. Hasta el próximo POST!

[Tips] Reactjs en Azure App Service

Servicios de mensajería en Azure

Hello world,

Hace un par de meses tuve la oportunidad de participar en la Global Azure Bootcamp de Barcelona con una charla sobre Event Grid. En el turno de preguntas, una de las dudas más extendidas fue sobre la diferencia entre Event Grid, Service Bus o Event Hub, otra sobre cuando es mejor utilizar uno u otro, y por último si son servicios compatibles entre ellos. La verdad que, aunque intenté responder de una forma rápida y sencilla, este tipo de cuestión podría dar para otra charla o mejor aún, para una mesa redonda donde poder debatir los pros y los contras de cada uno de los servicios, cosa que no descarto organizar en un futuro próximo, aunque de momentos, empezaré explicándolo con un post. Primero intentaré aclarar para que es cada uno de los servicios y como funciona, después expresaré mi opinión sobre cuándo sería mejor utilizar cada uno y por último que mejor que unos ejemplos para ver todo lo explicado anteriormente.

Entremos en materia, lo primero a destacar en los tres servicios, es que todos son compatibles entre ellos, es decir, podríamos utilizar un Service Bus para la gestionar los millones de billetes que vende mi compañía aérea, e integrarlo con Event Grid para que cada bloque de eventos que se produzca en el Service Bus sea notificado al responsable de ventas de mi compañía.

sbtoeventgriddiagram2
Integración Service Bus con Event Grid

Otra cosa importante que destacar, y de ahí vienen las dudas sobre si estos servicios sirven para lo mismo, es que todos ellos trabajan mediante la semántica de Publicación-Subscripción. Pero esto, no quiere decir que sirvan para lo mismo, o, mejor dicho, que estén pensados para lo mismo. Aunque en Azure una misma cosa se puede hacer de infinitas maneras, y pudiéramos pensar en que la frase de que “todos los caminos llevan a Roma” es cierta y valida, tenemos que pararnos a pensar, que el coste que nos puede repercutir el “llegar a Roma” puede ser demasiado alto, en cuanto a desarrollo, mantenimiento y en lo más importante, el COSTE.

Dicho esto, empezamos con la breve descripción de lo que es cada uno de los servicios:

Event Grid

eventgrid
Azure Event Grid

Es un servicio orientado a desarrollos que necesiten reaccionar a eventos. Estos eventos no solo pueden estar producidos por acciones de nuestra infraestructura, sino que también por nuestros desarrollos. Event Grid NO es una canalización ni un tratamiento de datos, para eso ya existen otros servicios, sino que es un enrutamiento de los eventos con los datos de ese evento, es decir, la información viaja en el evento (quien produjo el evento, la fecha, información de este, etc..). Este tipo de servicio lo englobaría en el mundo de la programación reactiva.

  • ¿Qué quiero decir con esto? Cada acción que se realiza en Azure o en un servicio de terceros, genera un evento, y mediante este servicio podremos administrarlos de una manera desatendida y totalmente abstraída, sin interferir en el funcionamiento de este.
  • ¿Como? Mediante un sistema de Publicación-Subscripción, es decir, hay “algo” que publica en algún sitio (topic) y hay “otro algo” que se subscribe a ese sitio.

Cabe destacar que este servicio es serverless, simplifica el consumo de eventos de una manera eficiente y fiable y consigue una entrega casi “real time“.

Service Bus

Azure Service Bus
Azure Service Bus

Este servicio está orientado a utilizarse cuando las aplicaciones que desarrollamos requieren de alto nivel de transaccionalidad, con un control exhaustivo de las mismas, ordenación, coherencia de los datos e incluso la detección de posibles duplicados. Funciona mediante un sistema de colas, de manera asíncrona y soporta mensajería estructurada de tipo FIFO (First In, First Out) y como los otros servicios, funciona con un sistema de Publicación-Suscripción

  • ¿Qué quiero decir con esto? Cada transacción que pasa por nuestra aplicación es considerada de gran valor. Haciendo que la pérdida de información o la duplicidad de esta pueda hacer que tengamos un gran problema.
  • ¿Como? Mediante un sistema de mensajería asíncrona. Almacena los mensajes en una cola, manteniéndolos ahí hasta que quien tenga que consumirlos esté preparado para hacerlo.

Como puntos fuertes de este servicio podríamos destacar la mensajería asíncrona de confianza, un procesamiento por lotes e incluso por sesiones, y algo tan interesante como la detección de mensajes duplicados de una manera autónoma.

Event Hub

EventsHub
Azure Event Hub

El servicio Event Hub al igual que Event Grid está orientado a eventos. La definición perfecta para este servicio sería “Plataforma de streaming distribuida”. Este servicio está pensado para el tratamiento masivo de datos. Recopila, transforma y almacena millones de eventos de una forma sencilla, permitiendo realizar un streaming de los mismos.

  • ¿Qué quiero decir con esto? Nos proporciona una solución que permite la recuperación de datos de una manera rápida para un procesamiento en tiempo real.
  • ¿Como? Al igual que Event Grid funciona mediante un sistema de Publicación-Subscripción.

La gran ventaja de este servicio frente a los otros dos, sería la retención de tiempo configurable, la baja latencia y la capacidad para recibir y procesar millones de eventos por segundo permitiendo leer estos desde varias aplicaciones mediante el sistema de Publicación-Subscripción. Este servicio estaría más orientado a tratamiento de telemetrías las cuales se necesitaría un procesamiento en tiempo real.

Mi opinión & ejemplos

Una vez aclarados los conceptos de cada uno de los servicios, vamos a ver en qué casos sería bueno utilizar unos u otros. Cabe destacar que entre ellos son complementarios e incluso hay casos en los que sería interesante poder jugar con ellos.

Event Grid

Utilizaría Event Grid cuando nuestro sistema tenga que reaccionar a algo, por ejemplo, imaginar que una aplicación “x” deja unos archivos en un storage y nosotros tenemos que recoger esos archivos y hacer algo con ellos. También es interesante utilizar este servicio, cuando el sistema ya está desarrollado y queremos extenderlo, ya que no tendríamos que tocar nada del sistema actual y podríamos seguir metiéndole lógica a la aplicación sin “estropear” nada de lo que ya está funcionando.

serverless-web-app
Diagrama Event Grid

Service Bus

Me plantearía meter Service Bus en mi proyecto cuando lo que necesite es solucionar un alto nivel de transacciones, en la que cada transacción sea vital, en la que no se pudieran repetir registros, imaginar, el servicio de transferencias de un banco, el cual tiene que soportar miles de transacciones al día, y cada una de esas transacciones ha de ser única, no se pueden repetir, ni se pueden perder. Y muy importante, utilizaría este servicio cuando la inmediatez de las acciones no fuera prioritaria.

about-service-bus-queue.png
Diagrama Service Bus

Event Hub

Este servicio lo utilizaría si lo que necesitase es conseguir dos cosas:

  1. Un procesamiento de datos “real time”.
  2. Procesamientos de millones de registros.

Esto se nos podría dar cuando lo que tengamos que hacer es procesar la telemetría que nos envía por ejemplo un coche de F1. El coche tiene mil sensores que cada segundo nos están bombardeando con información que tenemos que procesar y evaluar, para luego poder tomar decisiones sobre qué hacer el próximo Pit-Stop, o algo tan simple como decirle al piloto que baje el ritmo porque si no se quedará sin gasolina.

handle
Diagrama Event Hub

Y esto es todo amigos. ¡Nos vemos en el próximo post!

Servicios de mensajería en Azure

Como configurar HTTP/2 en nuestro App Service desde el portal de Azure

Hello world,

Hace justo un mes os contaba que el App Service de Azure se actualizaba para poder dar soporte al protocolo HTTP/2. Para poder configurarlo, teníamos que acceder mediante el explorador de recursos, a los archivos json de configuración del App Service y cambiar un valor de una clave de configuración.

Pues bien, desde ayer esta configuración está disponible desde el portal de Azure! Para acceder a ella solo tenemos que ir a la opción de “Configuración de la aplicación” y una vez dentro buscar la opción de “HTTP version“. Desde ahí podremos cambiar entre la versión 1.1 y las nueva 2.0.

Captura
Portal de azure, configuración de la aplicación

Si queréis saber más sobre las novedades que nos trae el protocolo HTTP/2, os animo a que os leáis el siguiente post:

HTTP/2 en nuestro Azure App Service

Hasta la próxima!

Como configurar HTTP/2 en nuestro App Service desde el portal de Azure

HTTP/2 en nuestro Azure App Service

Hello world,

Hace unos días el equipo de desarrollo de Azure, nos informaba de que introducían el esperado protocolo HTTP/2 para todas las apps desplegadas en App Services!

¿Qué es el protocolo HTTP/2?

Es el nuevo protocolo por el que se rigen las peticiones HTTP, dicho así, suena como algo que implica tener que modificar todas nuestras apps, pero nada más lejos de la realidad. Realmente no estariamos hablando de un “nuevo protocolo”, así que cambiaré esta definición y diré que es una actualización de nuestro “viejo” HTTP/1.1.

¿Qué novedades trae?

Sin entrar mucho en detalle porque no es lo que pretendo con este post, el objetivo de esta actualización no es otro que la optimización y con ello, conseguir mejorar el rendimiento de un protocolo, que ya había dado todo lo que podía dar. Para conseguir esto se han mejorado/cambiado las siguientes cositas:

  • Conexión única: En las versiones anteriores, para la carga de cualquier web, se utilizaban numerosas conexiones TCP para descargar el contenido de la misma(css, js, imágenes…). Con la llegada de la versión 2, solamente se realizará una única conexión, la cual será capaz de responder a múltiples solicitudes en paralelo.
  • Información redundante: Se elimina el envío de datos repetidos en cada petición, consiguiendo así, tener menos latencia en nuestras peticiones.
  • Multiplexación: palabra difícil, pero fácil de entender. En las versiones anteriores, el navegador tenia que esperar la respuesta de cualquier petición, antes de enviar otra… En la versión 2, esto se resuelve y permite el envío de varias peticiones sin tener que esperar la respuesta de la llamada anterior.
  • Servicio Server push: se podría decir que esto es como una cache, es decir, son una serie de estimaciones para que el servidor sea capaz de enviar la información al usuario antes de que este la solicite.
  • Compresión de cabeceras: en las versiones previas, las cabeceras no dejaban de ser texto plano, en esta versión se utiliza un sistema de compresión (HPACK). El cual hace que sean mucho más livianas.

Estas son, explicadas a groso modo, algunas de las novedades.

¿Qué tengo que hacer en mi App Service?

Vale, espero que hasta aqui os haya puesto los dientes largos y que queráis activar esto en vuestros App Services. Pues bien, os explicaré como, antes de nada, decir que de momento no existe una interfaz gráfica en Azure para hacerlo, sino que tendremos que entrar a toquetear los json de configuración de nuestra app.

El primer paso será buscar en la barra lateral la opción de “Explorador de recursos”:

Explorador de recursos
Explorador de recursos

Una vez dentro se nos abrirá un bonito editor, donde podremos ver todos los recursos de nuestra suscripción/es. En el árbol que se muestra a la izquierda tendremos que seleccionar:

subscription –> ResourceGroups –> providers –> Microsoft.Web –>sites –> [NUESTRO SITE] –> config –> web

Explorador de recursos2
Editor del explorador de recursos (árbol)

Una vez estemos en este archivo, solo tendremos que buscar la clave “http20Enabled” y ponerla a true, ojo, en las apps viejas, o mejor dicho, creadas antes de abril de 2018 el valor que tendrá será por defecto a false, en las nuevas que nos creemos, este valor ya viene a true.

http20Enabled
Editor json configuración web app

Por último, para que surja efecto el cambio tendremos que reiniciar la web app. OJO, yo soy más partidario de pararla, esperar unos segundos y volver a arrancarla.

Espero que el post os haya servido como una introducción a este “nuevo” protocolo así como para que sepáis configurar vuestros App Services hacia el nuevo protocolo.

Saludos y ¡hasta la próxima!

HTTP/2 en nuestro Azure App Service

Testeo aplicación ASP.net MVC con Selenium (Parte 2)

Hello world,

Continuando con la serie de posts sobre testeo de aplicaciones ASP.net MVC, hoy toca hablar sobre algo que muchas veces a los desarrolladores se nos olvida, o mejor dicho, pasamos por alto: QUE FUNCIONE EN TODOS LOS NAVEGADORES!

Siguiendo el dicho de “cada maestrillo tiene su librillo”, en los desarrolladores web pasa algo parecido, pero con los navegadores. Cada cual trabaja con el que más a gusto se siente… Chrome, Firefox, Edge… Internet Explorer (vale sí, no creo que nadie se sienta a gusto con este…). Por ejemplo en mi caso, siempre utilizo Chrome, me gustan las herramientas de desarrolladores que tiene, sencillas y claras de utilizar, y también por la gran cantidad de extensiones que le puedes instalar. En definitiva, que aunque todos sigamos las convenciones, empleemos buenas prácticas, etc… no podemos asegurar que cuando vayamos a desplegar nuestra web funcione en todos y cada uno de ellos como esperamos. Evidentemente, cuando estamos desarrollando NO probamos en todos, sino los desarrollos se eternizarían, pero si que antes de pasar al equipo de QA, deberia haber pasado unas pruebas mínimas en todos los navegadores. Ya que como desarrollador, y pese a contar con el comodín del equipo de QA, no gusta que estos encuentren bugs en nuestros desarrollos. Una vez soltado todo este rollo, vamos a entrar en materia.

En el primer post, hablamos de como crearnos una clase base donde irá la configuración del IIS Express, una vez hecho esto, lo siguiente sería continuar preparando la base de los test para que se puedan lanzar en uno o varios navegadores. Para ello, tendremos que empezar por descargarnos los webDrivers de cada navegador:

Una vez descargados, debemos incluirlos en nuestra solución de test (imagen 1). Posteriormente haremos “botón derecho” sobre cada uno de ellos, seleccionar la opción de propiedades y decirle que se copien a la carpeta de compilación (imagen 2). En mi caso, siempre le pongo que se copie si es una versión más reciente, ya que no me interesa que se copie cada vez, sino, solo cuando haya actualizado la versión de los drivers.

Imagen 1 (Archivos en proyecto de test)
Imagen 1 (Archivos en proyecto de test)

Captura2
Imagen 2 (Propiedades archivos)

Si no hacemos el paso anterior, al ejecutar nuestros test, nos daría una bonita excepción, diciéndonos que no puede encontrar los drivers, y lo que es un detalle, en la excepción nos dice la url donde descargar cada uno de ellos.

Continuamos…

Lo siguiente es configurar los distintos drivers de navegadores para poder ejecutar las pruebas. Para ello bastará con importar los correspondientes usings que nos proporciona la librería de selenium:

using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Edge;
using OpenQA.Selenium.Firefox;

Con esto ya podríamos crearnos los objetos necesarios para cada navegador. En mi caso, y siguiendo lo que comentaba en el post anterior, tengo una clase base, y en el TestInitialize es donde instancio dichos objetos para tenerlos disponibles desde cualquier test:

public ChromeDriver ChromeDriver { get; set; }
public FirefoxDriver FirefoxDriver { get; set; }
public EdgeDriver EdgeDriver { get; set; }

[TestInitialize]
public void TestInitialize()
{
    // Start IISExpress
    StartIIS();

    // Start Selenium drivers
    ChromeDriver = new ChromeDriver();
    FirefoxDriver = new FirefoxDriver();
    EdgeDriver = new EdgeDriver();
}

Con esto ya tendríamos casi casi completa la clase base de nuestros tests, donde lanzaremos el IIS Express, y tendriamos las instancias de los distintos navegadores sobre los que quisiéramos probar nuestra web. Quedaría como dije en el primer post, “limpiar” todo lo que hemos hecho hasta el momento, en este caso, seria “matar” las instancias de los distintos navegadores. Para ello en el TestCleanup de la clase base tendríamos que poner lo siguiente:

[TestCleanup]
public void TestCleanup()
{
     // Ensure IISExpress is stopped
     if (_iisProcess.HasExited == false)
     {
         _iisProcess.Kill();
     }

     // Stop all Selenium drivers
     ChromeDriver.Quit();
     FirefoxDriver.Quit();
     EdgeDriver.Quit();
}

To be continued…

Testeo aplicación ASP.net MVC con Selenium (Parte 2)

Testeo aplicación ASP.net MVC con Selenium (Parte 1)

Hello world,

Cuando hacemos las estimaciones de un proyecto, siempre se nos olvida una de las partes mas importantes, a la vez que mas desagradecidas e incluso podría decir, que más costosas… Esto son los test, si si, los test, eso que siempre queremos hacer pero que “nunca” hacemos…

Hoy empiezo una serie de post en los que me gustaría explicar como hacer testing funcional en nuestra web, desarrollada en ASP.net MVC. En este primero hablaré de como montar las base de nuestros test, así como las librerías a utilizar.

Vamos al tema… Lo primero que tenemos que tener claro (sin entrar demasiado en detalle) es que un test se compone de 3 partes:

  1. TestInitialize: Aquí inicializaremos todo lo que necesitemos para que nuestro test se pueda ejecutar (recordad que un test tiene que probar “algo” en un entorno controlado). Imaginad que nuestro test lo que hace es ejecutar una pantalla que modifica un valor de una entidad de base de datos, evidentemente no modificaremos algo que ya esté creado, con lo que aquí podremos crearnos una entidad (entorno controlado) a la que después modificaremos desde el punto 2.
  2. TestMethod: Aquí pondremos el código necesario para probar nuestra casuística, en el ejemplo explicado en el punto anterior, modificaríamos algo de la entidad creada y validaríamos que esa modificación se ha realizado correctamente.
  3. TestCleanup: Como dijimos en el punto 1, un test se realiza sobre un entorno controlado, para ello hemos realizado una serie de acciones en el punto 1, y otras tantas en el punto 2 y es aquí donde tenemos que deshacer, o mejor dicho, volver al estado de antes de haber lanzado nuestro test. (Este punto cobra especial sentido cuando estamos haciendo test funciona o test de integración).

Hecha esta pequeña aclaración, vamos a continuar con el tema… Cuando vamos a hacer un test funcional tenemos que tener claro que lo que vamos a probar es un flujo de nuestra aplicación. Por ejemplo, quiero probar que funciona el alta de usuario, en el que yo relleno una serie de campos y por ultimo le doy a un botón “guardar”. Muy bien, ya tenemos decidido cual será nuestro primer test. Ahora llega lo divertido, ¿Cómo puedo probar eso sin yo tener que interactuar con la aplicación? ¿puedo hacerlo programaticamente?, mi web corre sobre un IISExpress que se lanza cuando le doy al “play” del Visual Studio ¿Cómo puedo simular eso? y en el mismo sentido, ¿sobre que navegador correrán mis test?… Como dijo Jack el destripador, vamos por partes!

Lo primero, existen librerías que nos proporcionan el poder realizar este tipo de testing, en este caso yo voy a utilizar Selenium. Para ello solo tendremos que ir al administrador de paquetes Nuget de nuestro proyecto de test e instalar “Selenium.WebDriver”:

selenium_nuget
Administrador de paquetes nuget VS2017

Una vez instalado el paquete, lo siguiente que tenemos que pensar es como vamos a lanzar nuestros test y sobre que navegadores queremos que corran dichos tests. En mi caso he decidido que se lancen sobre un IISExpress y se ejecuten en Google Chrome(veremos como configurar varios navegadores). Para ello en el TestInitialize levantaré una instancia de IISExpress:

private Process _iisProcess;
const int _iisPort = 9515;
[TestInitialize]
public void TestInitialize()
{
     // Start IISExpress
     StartIIS();
}
private void StartIIS()
{
     // Obtain application path
     var applicationPath = GetApplicationPath("MyApplication");
     var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
     
     // create instance of IIS
     _iisProcess = new Process();
     // Set Path of instalation ISSExpress in the machine
     _iisProcess.StartInfo.FileName = $@"{programFiles}\IIS Express\iisexpress.exe";
     // set the application path to atach the ISS and the port
     _iisProcess.StartInfo.Arguments = $" /path:{applicationPath} /port:{_iisPort}";
     // Start the IIS
     _iisProcess.Start();
}
protected virtual string GetApplicationPath(string applicationName)
{
     var solutionFolder = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory)));
     return Path.Combine(solutionFolder, applicationName);
}

Con esto ya tendríamos una instancia de IISExpress sobre la que correr nuestros test. Este código seria interesante implementarlo en una clase base ya que lo utilizaremos desde todos los test que queramos realizar con selenium.

Si recordáis al principio del post, comentaba que los test constan de 3 partes, el initialize, el test y el Cleanup. En este caso seria interesante configurar un Cleanup en el que matemos nuestra instancia de IISExpress.

[TestCleanup]
 public void TestCleanup()
 {
      // Ensure IISExpress is stopped
      if (_iisProcess.HasExited == false)
      {
          _iisProcess.Kill();
      }
 }

Con este simple código ya tendríamos la configuración del IISExpress para que se  puedan lanzar nuestros tests. En el próximo post veremos como configurar los drivers para los distintos navegadores sobre los que corran nuestros tests.

Saludos y hasta el próximo post!

Testeo aplicación ASP.net MVC con Selenium (Parte 1)