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!

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

Métodos de Extensión, pon un “this” en tu desarrollo

Hello world!

¿Cuantas veces nos ha pasado que tenemos que agregar funcionalidad a un tipo ya existente? O peor aún ¿que pasa si tenemos que modificar algo también existente que no sabemos que repercusiones tendrá? y ¿si tenemos que mapear entre objetos? Estas y algunas otras preguntas son las que intentaré destripar en este post.

Vamos con un poquito de literatura, ¿Qué son los métodos de extensión? Según la msdn:

Los métodos de extensión permiten “agregar” métodos a los tipos existentes sin crear un nuevo tipo derivado, recompilar o modificar de otra manera el tipo original. Los métodos de extensión son una clase especial de método estático, pero se les llama como si fueran métodos de instancia en el tipo extendido.

Esta muy bien la definición, pero no acabo de ver esto de los métodos de extensión, ni como aplicarlo a mi código… Y si os digo .ToString()string.IsNullOrWhiteSpace(xxxx), y muchos otros que seguro que habéis utilizado millones de veces y no os habíais parado a pensar que esa funcionalidad “extra” de los tipos que la nos proporciona el propio framework de .NET, podríamos aplicarla a nuestros tipos…

Empezaremos declarando la  clase Phone:

public class Phone
    {
        public Guid Id { get; set; }
        public string SerialNumber { get; set; }
        public string Model { get; set; }
        public string Description { get; set; }
        public List Properties { get; set; }
    }

En esta clase lo primero que se me ocurre es querer validar si tiene informadas Properties  o no… ya que a la larga tendremos que hacer mil veces la validación de si tiene registros, con la típica doble validación de if(phone.Properties != null && phone.Properties.Count > 0). la verdad que ese if metido en cada uno de los sitios desde donde tengamos que utilizar nuestra clase Phone no es demasiado “elegante”. ¿Como resolveríamos esto de una forma mas elegante? pues extendiendo nuestra clase de la siguiente manera:

public static class PhoneExtension
    {
        public static bool HasProperties(this Phone source)
        {
            if (source.Properties != null && source.Properties.Count > 0)
                return true;
            else
                return false;
        }
    }

Es esta clase podemos ver 2 puntos clave, uno es que la clase tiene que ser estática, y el otro es el this que ponemos en el parámetro del método, este especifica para que tipo va a funcionar este método, es decir, este método pertenece al tipo “xxxx”.

Otra de las cosas para las que se puede utilizar esto, es para el mapeo entre diferentes tipos, por ejemplo para convertir tu dto a tu viewModel. Vale si, me diréis que actualmente ya hay librerías como AutoMapper que me lo hace “automaticamente”, pongo esto entre comillas porque efectivamente te lo hace “automaticamente” siempre y cuando cumpla la convención de nombres… Pero eso no siempre es así, o incluso hay veces que nos va a interesar tener un control mas exhausto del mapeo. Veamos un ejemplo:

En este caso nos crearemos una clase PhoneDto que tendrá solamente 2 propiedades que serán las que tengamos que transportar entre capas:

public class PhoneDto
{
      public string SerialNumber { get; set; }
      public string Description { get; set; }
}

Dentro de la misma clase estática que nos habíamos creado previamente, podemos incluir algo como esto:

public static PhoneDto ToPhoneDto(this Phone source)
{
      if (source == null) return null;
      var result = new PhoneDto();
      result.Description = source.Description;
      result.SerialNumber = source.SerialNumber;
      return result;
}

Y con esto ya tendríamos nuestro mapper implementando, simplemente extendiendo nuestro modelos de datos.

Por ultimo quedaría ver como utilizarlo. Pues es tan simple como nombre del objeto “punto” y esto ya nos mostrará el intelligence (OJO, hay que añadir el using*) con todas las extensiones/propiedades que tenga nuestro objeto!

extension
Intelligence VS mostrando la extensión

Hasta VS 2015, se tenia que añadir el using a mano… pero a partir de esta versión, con la combinación de teclas “Ctrl + .” ya te sabe reconocer y proponer tus propias extensiones para importarlas como using.

Espero que os resulte útil!

Un saludo y hasta pronto!

Métodos de Extensión, pon un “this” en tu desarrollo

MVC: ActionLink a otro controlador #MERGE

Hello world,

Desarrollando una de mis primeras paginas con MVC, me he encontrado con el problema de tener que hacer una llamada a un controlador distinto al que tiene mi vista. Como seguramente todos sabéis, para hacer la llamada a la misma vista es tan simple como muestro en el siguiente ejemplo:

@Html.ActionLink("TextoDelLink", "NombreDelAction", "NombreDelControlador", new { id = item.id })

Con esto ya tendríamos creado nuestro link a una nueva vista, siempre y cuando esta esté dentro del mismo controlador. Ahora bien, si lo que necesitamos es llamar a una de vista de otro controlador, seria suficiente con añadir un parámetro mas, el valor de este parámetro seria null, quedando de la siguiente manera:

@Html.ActionLink("TextoDelLink", "NombreDelAction", "NombreDelControlador", new { id = item.id }, null)

Para los no puestos en materia, explicaré cada uno de los parámetros:

  • TextoDelLink: Este es el texto que se muestra en el link.
  • NombreDelAction: Nombre del Action que hemos creado en nuestro controlador.
  • NombreDelControlador: Nombre del la clase del controlador.
  • new { id = item.id }: Lista de parámetros que queremos pasar a la nueva vista.

Con estas líneas tendremos montados nuestros links a otras vistas, tanto del mismo controlador, como de distinto controlador.

MVC: ActionLink a otro controlador #MERGE

Presentación…

Hello world,

Después de un tiempo apartado del mundo de los blogs, me ha vuelto a picar el gusanillo de volver a escribir. Lo primero que haré será traspasar las entradas de mi viejo blog a este.

Una vez hecho esta breve introducción, me voy a presentar. Soy Nacho Fanjul, Senior developer en tecnología .NET en el departamento de proyectos de Pasiona, llevo en este mundo de la programación casi 10 años y como os podéis imaginar me las he encontrado de todos los colores… No voy a entrar a repasar mi trayectoria profesional, para eso podéis visitar mi “LinkedIn”, sí que resumiré lo que llevo haciendo los últimos tiempos.

Me he especializado en desarrollo de aplicaciones web, desarrollando principalmente con MVC, aunque también me ha tocado “pelearme” con webForms. En cuanto a la parte de “BackEnd”, he ido acumulando experiencia en codeFirst, Entity Framework, arquitecturas DDD, etc…

Últimamente, me estoy introduciendo en el mundillo de azure, donde aún me queda un largo recorrido…

¿Qué encontrareis en este blog?

Esto es simple, iré poniendo las cosillas que me vaya encontrando en el día a día, o simplemente cosas nuevas que vaya aprendiendo.

En fin, no me enrollo más, solo deciros que espero que este blog os resulte útil, y podáis aprender algo y que yo también aprenda algo!

Saludos a tod@s!

 

Presentación…