Windows Phone 7.5:Calidad del software, pruebas unitarias y ventas.

Hola a todos!

Hoy vamos a hablar un poco sobre las pruebas unitarias en Windows Phone 7.5. Es un tema del cual no existe mucha información ni guías sobre como llevarlas a cabo y que, extrañamente, se deja de lado normalmente. Como si no fuesen necesarias al tratarse de aplicaciones móviles.

Nada más lejos de la realidad. Creo que una de las frases que he escuchado últimamente que más sentido tiene es: “La calidad no es opcional”. En Plain Concepts esto es un mantra que nos repetimos constantemente.

Introducción

Todo proceso industrial moderno que tenga como objetivo colocar un producto en el mercado para su consumo, más o menos masivo, tiene un cierto nivel de calidad exigido. No quiero pensar que pasaría si cuando un fabricante de coches termina una unidad la pusiese directamente en el concesionario sin probarla en absoluto, o simplemente la arrancase para ver que el motor funciona con el acelerador y que el encendido / apagado no provoca una explosión. Todos sabemos que esto no es así. Los vehículos, juguetes, medicamentos, utensilios varios, etc… pasan un complicado entramado de pruebas internas y externas, certificaciones de seguridad y tienen un conjunto de regulaciones y certificaciones que cumplir. Gracias a esto, los coches no explotan.

Y ¿que pasa con el software? El software en el 90% de los casos sale a la venta sin una sola prueba… y cuando un usuario presiona “B” antes que “A” el PC explota por completo.

Esto se agrava más si cabe en el caso de las aplicaciones móviles. ¿Porque? ¿No se supone que son aplicaciones más sencillas y fáciles de desarrollar? El problema de las aplicaciones móviles no es tanto su tamaño o complejidad, es su ciclo de vida. Una aplicación móvil que puede ser consumida por millones de personas (por que si desarrollamos una aplicación móvil es para vender mucho ¿No?) en diferentes dispositivos, idiomas, redes de telefonía y condiciones de batería debe cumplir una premisa básica para tener éxito y mantenerlo: un ciclo de vida entre actualizaciones corto e incremental. Debemos actualizar nuestra aplicación de forma periódica, añadiendo características y corrigiendo bugs. Si tenemos éxito en las ventas empezaremos a tener competencia, que intentará desbancarnos ofreciendo más características, más eficiencia y menos fallos.

Es aquí cuando nos damos cuenta de que necesitamos de alguna forma controlar la calidad de nuestra aplicación. En un código tan expuesto a evolucionar y cambiar, un pequeño cambio en la clase CondensadorDeFluzo.cs puede provocar un error en la clase ViajeEnElTiempo.cs sin que seamos conscientes de ello. Hacemos dos o tres pruebas manuales sobre la funcionalidad nueva o cambiada y como todo va bien, enviamos a certificación, un proceso que puede tardar varios días. Cuando los usuarios se descargan nuestra aplicación, empezamos a recibir informes de fallos en la funcionalidad expuesta por ViajeEnElTiempo.cs, lo arreglamos, probamos y volvemos a repetir el proceso de certificación, vuelven a pasar los días. Seguimos recibiendo informes de error, en este caso en otra parte remota y olvidada de nuestro código, que no tocamos desde las primeras versiones, repetimos el mismo proceso, otra vez… lo que conocemos normalmente como Regresiones, que cualquiera acostumbrado a mantener un producto conocerá / sufrirá perfectamente.

Estamos en un circulo vicioso, cada vez cuesta más actualizar una funcionalidad sin que rompamos otra y los usuarios empiezan a percibir una constante: Nuestra aplicación es un asco porque con cada nueva versión se rompe algo. Con ello, empezamos a perder clientes y pronto, pasamos de estar ganando dinero a tener 0 descargas y 0 usuarios activos. ¿Ha fracasado nuestra aplicación? ¿O hemos fracasado en nuestro papel de fabricantes de un producto?. Sin duda, hemos fracasado en nuestro papel de fabricantes porque hemos descuidado la parte más importante del proceso: LA CALIDAD.

¿Qué podemos hacer? No existen balas de plata. Pero si pudiésemos de alguna forma implementar un sistema automático que se encargase de probar nuestro código bajo demanda. Al cual pudiésemos indicarle los parámetros para probar los métodos, que esperar y bajo que condiciones consideramos que nuestro código es funcional y correcto, evitaríamos escenarios como el expuesto más arriba y podríamos ampliar y corregir funcionalidades, asegurándonos que la aplicación sigue funcionando correctamente o encontrando los fallos de forma rápida antes de que lleguen al usuario. De hecho, ahora que lo pienso así… si que hay algo que hace esto: LAS PRUEBAS UNITARIAS.

Pruebas unitarias

Sobre las pruebas unitarias he oído de todo: que no sirven para nada, que son una perdida de tiempo, que como le vendes al cliente el tiempo que tardas en hacerlas… Todos somos mayorcitos, cada uno que desarrolle como quiera, pero plantearos esto: No puedes ir a un concesionario y pedir que te vendan un coche que no haya pasado las pruebas de seguridad y certificaciones elegidas por la ley, porque no hay de esos. Simplemente no existen y, es más, las compañías presumen de cuanto invierten en probar sus modelos. Sabes que una parte del precio que te cuesta, paga esas pruebas. Este es el truco: las compañías de coches NO GASTAN en pruebas, INVIERTEN en pruebas.

Así que, vamos a emular a la industria de la automoción, invirtiendo en dotar a nuestras aplicaciones de una calidad sobresaliente. Para ello vamos a desarrollar una aplicación Windows Phone 7.5. Es muy importante para poder realizar pruebas unitarias que usemos el patrón MVVM en nuestra aplicación. ¿No lo conoces? Mira estos artículos en mi blog que lo explican en detalle: aquí y aquí

Empezaremos con el diseño de la aplicación, muy sencilla. Esta aplicación nos permitirá escribir un nombre y pulsar un botón para que nos salude. Esta sería nuestra View:

    <StackPanel>
        <TextBlock Text="Enter your name:"
                    FontSize="{StaticResource PhoneFontSizeLarge}"
                    Margin="12,0,12,0">
        </TextBlock>
        <TextBox Text="{Binding Name, Mode=TwoWay}">
        </TextBox>
        <Button Content="Say Hello!"
                Command="{Binding HelloCommand}"
                CommandParameter="{Binding Name}">
        </Button>
    </StackPanel>

 

A continuación, necesitamos crear nuestra ViewModel, para ello, en vez de hacerlo en la misma aplicación Windows Phone, vamos a crear una nueva librería de clases de Windows Phone:

image

 

En este nuevo proyecto en primer lugar vamos a crear una clase llamada CommandHello que implementará la lógica de nuestro comando:

public class CommandHello : ICommand
{

    bool ICommand.CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    void ICommand.Execute(object parameter)
    {
        string name = parameter as string;

        MessageBox.Show(string.Format("Hello {0}", name));
    }
}

 

Es una clase muy sencilla, simplemente muestra un MessageBox usando para ello el parámetro que le ha sido indicado.

A continuación vamos a crear la ViewModel de nuestra View:

public class VMMainPage : INotifyPropertyChanged
{
    //Ctor
    public VMMainPage()
    {
        Name = "Yeray";
    }

    private string name;
    public string Name 
    {
        get { return name; }
        set 
        { 
            name = value;
            RaiseChange("Name");
        }
    }

    private ICommand helloCommand;
    public ICommand HelloCommand
    {
        get
        {
            if (helloCommand == null)
                helloCommand = new CommandHello();
            return helloCommand;
        }
    }

    //INotifyPropertyChanged Implementation

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaiseChange(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

 

Esta ViewModel es muy simple, implementa el interface INotifyPropertyChanged para poder notificar a la UI de cambios en las propiedades y expone dos propiedades: Name, donde contendremos el nombre indicado por el usuario y HelloCommand, una instancia de nuestro comando para ejecutar el código al presionar el botón.

Ahora ya hemos escrito código y podemos empezar a probarlo, antes incluso de enlazarlo con la interface de usuario. Necesitamos asegurarnos de que el código es funcional y se comporta como deseamos que lo haga.

Vamos a añadir un nuevo proyecto a la solución que tenemos creada, en este caso del tipo Test Project:

image

 

Lo primero que necesitamos hacer es añadir una referencia a nuestra librería de clases, donde se encuentra el código que hemos escrito. Hacemos click derecho sobre el proyecto de Test y seleccionamos la opción “Add reference” en la pestaña de proyectos seleccionamos “PhoneViewModels” y aceptamos.

En este paso observaremos algo extraño:

image

No tenemos que asustarnos, es simplemente un aviso de que el framework del proyecto de Test (.NET Framework 4) no coincide con el de PhoneViewModel (Silverlight 4). Solo tendremos que cerrar y volver a abrir la solución para que este aviso desaparezca. Aún así la referencia funcionará correctamente y sin darnos quebraderos de cabeza. Simplemente se trata de un aviso.

En este caso, como vamos a usar una clase que implementa el interface ICommand, debemos añadir una referencia al ensamblado System.Windows.dll, que podemos encontrar en:

C:Program Files (x86)Reference AssembliesMicrosoftFrameworkSilverlightv4.0ProfileWindowsPhone71

Para comenzar vamos a crear las pruebas para la clase CommandHello. Añadimos un nuevo test a nuestro proyecto de test, botón derecho sobre el proyecto PhoneTest y seleccionamos Add > New Test:

image

Seleccionamos la plantilla “Basic Unit Test” y establecemos como nombre: nombre de la clase a probar + Test, de esta forma podremos mantener una estructura similar al proyecto que estemos probando. Una vez creado, veremos el archivo de código de la clase CommandHelloTest:

[TestClass]
public class CommandHelloTest
{
    [TestMethod]
    public void TestMethod1()
    {
    }
}

Por defecto se crea un método “TestMethod1” que podemos (debemos) borrar. La única diferencia que veremos con otras clases es que la clase se encuentra decorada con el atributo “TestClass” que indica que contiene pruebas y los métodos se decoran con el atributo “TestMethod” para indicar que son pruebas.

Antes de continuar, añadimos a esta clase un using a PhoneViewModels, el namespace donde están nuestras ViewModels.

Ahora vamos a empezar a probar nuestra clase CommandHello, una de las primeras cosas que desearíamos probar es que el método CanExecute de CommandHello devuelve el valor adecuado. Por defecto solo podremos acceder a los métodos públicos de una clase, por lo que vamos a modificar la clase CommandHello para que sus métodos sean públicos:

public class CommandHello : ICommand
{
    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        string name = parameter as string;

        MessageBox.Show(string.Format("Hello {0}", name));
    }
}

 

De esta forma podremos acceder tanto al método CanExecute como al método Execute y probarlos, vamos a realizar una prueba unitaria (muy simple) para el método CanExecute:

[TestMethod]
public void CanExecuteTest()
{
    CommandHello target = new CommandHello();

    bool actual = target.CanExecute(null);

    Assert.IsTrue(actual);
}

 

Como podemos ver, nuestro método de prueba sigue la misma nomenclatura que nuestra clase: nombre del método + Test. Los nombres de todas las pruebas deben ser únicos, si quisiésemos realizar varias pruebas a un mismo método, probando distintas situaciones podríamos usar una nomenclatura: nombre del método _ Descripción de la prueba _ Test (por ejemplo: CanExecute_ReturnFalse_Test).

Ahora podemos comprobar si realmente funciona nuestro código de la forma que esperamos, devolviendo True cuando llamemos a CanExecute, para ello ejecutamos la prueba unitaria, podemos hacerlo pulsando con el botón derecho sobre el código y seleccionando la opción “Run Tests” lo que nos abrirá la pantalla de resultados de tests y ejecutará el test actual:

image

 

Esta pantalla nos indica que nuestro Test se ha pasado con éxito, es decir, nuestro código se comporta como esperamos en la prueba. si modificamos el test, por ejemplo para esperar que devuelva False:

[TestMethod]
public void CanExecuteReturnFalseTest()
{
    CommandHello target = new CommandHello();

    bool actual = target.CanExecute(false);

    Assert.IsFalse(actual);
}

 

El resultado al ejecutar el test será el siguiente:

image

Nuestro primer test ha funcionado, pero el nuevo que hemos escrito ha fallado, y nos indica que el fallo se debió a que el Assert.IsFalse no se cumplió. Con esto ya tendríamos una pista rápida de que nuestro código no se comporta como deseamos.

Estos test son muy simples, solo para que se entienda la mecánica con la que se pueden realizar tests en Windows Phone de la misma forma que en cualquier otro tipo de proyectos. Lo mejor de esta forma es que, si disponemos de un sistema de integración continua, estos test serán totalmente compatibles con el y podremos pasarlos de forma automatizada al realizar la integración de nuestro código.

A continuación os dejo el proyecto de ejemplo para que juguéis con el y os sirva de referencia.

 

Un saludo a todos y Happy Coding!!

Windows Phone 7.5: Tareas en segundo plano (2 de 2)

Hola a todos!

En el post anterior vimos como trabajar con una parte del api de Acciones Programadas: las notificaciones programadas. Si te lo perdiste, puedes verlo aquí.

En este segundo post vamos a aprender a trabajar con tareas programas. Estas tareas programadas nos permiten ejecutar código en segundo plano y sin tener nuestra aplicación iniciada. A la hora de usar las tareas programadas podremos elegir entre dos tipos diferentes, las tareas periódicas, basadas en el objeto PeriodicTask, o las tareas intensivas, basadas en el objeto ResourceIntensiveTask.

Microsoft ha dejado muy claro varias veces que no desea que nuestro terminal se quede sin batería o se sature debido a procesos en segundo plano. Debido a esto, existen ciertas limitaciones en el uso de tareas programadas que deberemos tener muy en cuenta al usarlas. En primer lugar, común a ambos tipos de tareas, nos encontraremos con una limitación en el consumo de memoria. En cualquier momento de su ejecución, nuestra tarea programada no puede consumir más de 5Mb de memoria, si superamos este límite, el sistema se encargará, automáticamente y sin previo aviso de terminar la ejecución de la tarea. También tenemos un tiempo de vida máximo, al igual que en las notificaciones. Disponemos de una propiedad ExpirationTime, pero en este caso, la fecha nunca podrá ser superior a dos semanas desde la fecha establecida en la propiedad BeginTime.

También nos encontraremos con restricciones en las apis que podemos usar desde las tareas programadas:

Namespace

Api

Microsoft.Devices
Camera
VibrateController
NowPlaying
Microsoft.Devices.Radio
Namespace completo
Microsoft.Devices.Sensonrs
Namespace completo
Microsoft.Phone.BackgroundAudio
BackgroundAudioPlayer
Microsoft.Phone.BackgroundTransfer Add(BackgroundTransferRequest)
Microsoft.Phone.Controls WebBrowser
Microsoft.Phone.Info IsKeyboardDeployed
Microsoft.Phone.Scheduler Add(SchedulerAction)
Micrososft.Phone.Tasks
Namespace completo
Microsoft.Xna Namespace completo
System.Windows MessageBox
System.Windows.Controls MediaElement
MultiScaleImage
System.Windows.Media LicenseAcquirer
A/V Capture
System.Windows.Navigation Namespace completo

Duración, intervalo y límite de instalación

A nivel particular, cada uno de los tipos de tareas programadas cuenta con restricciones en la duración de ejecución, el intervalo de lanzamiento y el límite de tareas del mismo tipo que soporta Windows Phone 7.5.

Las tareas periódicas, PeriodicTask, tienen una duración máxima por ejecución de 15 segundos, tiempo después del cual es terminada por el sistema automáticamente. Cuentan con un intervalo de lanzamiento de 30 minutos, que puede sufrir desviaciones hasta de 10 minutos. Esto es así porque, para ahorrar batería, el sistema agrupa la ejecución de varias tareas periódicas. Tenemos un límite por dispositivo, sin determinar de forma general, que puede ser tan bajo como 6 tareas periódicas. Además pasado cierto número de tareas periódicas, el sistema avisará al usuario de que su batería se consumirá más rápidamente por tener múltiples tareas en segundo plano. Por último, si se activa el modo de ahorro de batería, todas las tareas periódicas se cancelan automáticamente.

Las tareas intensivas, ResourceIntensiveTask, tienen una duración mayor de ejecución, de hasta 10 minutos. Al igual que las tareas periódicas, pasado el tiempo máximo de ejecución, nuestra tarea será terminada por el sistema. En este caso, el sistema no agrupa las tareas, lanza cada una de forma independiente y mientras se está ejecutando una tarea intensiva no se pueden ejecutar otras, lo que puede provocar que nuestra tarea no se lance en ciertos momentos que debería. A este hecho hay que añadir ciertas restricciones muy severas sobre el estado del dispositivo, así, para que nuestra tarea se ejecute debemos cumplir tres requisitos: estar conectados a una red Wi Fi, estar conectados a alimentación de energía externa y que el nivel de batería se sitúe por encima del 90%.

Por todas estas limitaciones en el uso de las tareas en segundo plano, debemos estar preparados para una posibilidad: que no se ejecuten nunca o que no lo hagan cuando nosotros hemos ordenado. Tenemos que controlar este hecho y preparar nuestra aplicación para tomar las medidas necesarias para seguir trabajando sin la ejecución de las tareas.

Creación de una tarea en segundo plano

Como regla general, una aplicación Windows Phone solo puede definir una tarea de ejecución en segundo plano, más conocida como agente. La parte buena de esto es que un mismo agente puede ser registrado como tarea periódica o intensiva, solo cambiando la forma en la que la activamos y lanzamos.

Lo primero que necesitamos es crear un nuevo proyecto de Silverlight para Windows Phone y a continuación añadir a la solución un proyecto del tipo Windows Phone Scheduled Task Agent:

image

 

Este será el agente que usaremos para ejecutar nuestro código en segundo plano. Solo tenemos que añadir una referencia en nuestra aplicación al agente, usando el menu Add Reference y seleccionando el proyecto del agente en la pestaña “projects”.

Si abrimos el archivo ScheduledAgent.cs de nuestro agente veremos que ya existen una serie de métodos creados:

  • OnInvoke: Cuando el sistema comience la ejecución del agente, llamará a este método.
  • ScheduledAgent_UnhandledException: En este método recibiremos todas las excepciones no manejadas por nuestro código.
  • ScheduledAgent: El constructor de la clase.

Si nos fijamos en el método OnInvoke veremos lo siguiente:

protected override void OnInvoke(ScheduledTask task)
{
    //TODO: Add code to perform your task in background

    NotifyComplete();
}

Por defecto tenemos una llamada al método NotifyComplete, este método se encarga de notificar al sistema que se ha terminado la ejecución del agente, es importante llamarlo o el sistema no sabrá que hemos concluido y, pasado el tiempo máximo terminará automáticamente el proceso.

Como vimos anteriormente, en un agente no tenemos soporte para comunicarnos con el usuario usando la clase MessageBox, por ello, vamos a usar la clase ShellToast para poder enviar un aviso al usuario cuando se llame al método OnInvoke., lo primero que debemos hacer es añadir un using a Microsoft.Phone.Shell en nuestro agente y a continuación crear el mensaje y mostrarlo:

protected override void OnInvoke(ScheduledTask task)
{
    //TODO: Add code to perform your task in background
    ShellToast mensaje = new ShellToast();
    mensaje.Title = "Notificación";
    mensaje.Content = "Hola desde una tarea";
    mensaje.NavigationUri = new System.Uri("/MainPage.xaml", System.UriKind.Relative);
    mensaje.Show();
    NotifyComplete();
}

Lo mejor de la clase ShellToast es que, además de facilitar un mensaje al usuario, podemos definir una Uri de nuestra aplicación a la que el usuario será dirigido cuando pulse sobre la notificación, pudiendo de esta forma usar el agente como una forma de comunicar información al usuario y que sea capaz de manera sencilla de ir hacia ella.

Ahora que ya tenemos creado nuestro agente, vamos a iniciarlo desde nuestra aplicación Windows Phone.

Vamos a crear un StackPanel con dos botones, uno para registrar el agente como una tarea periódica y otro como intensiva:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <StackPanel>
        <Button Name="btnPeriodic" Content="Tarea periódica"
                Click="btnPeriodic_Click"></Button>
        <Button Name="btnIntensive" Content="Tarea intensiva"
                Click="btnIntensive_Click"></Button>
    </StackPanel>
</Grid>

Primero vamos a añadir un using a nuestro agente y a Microsoft.Phone.Scheduler:

using Microsoft.Phone.Scheduler;
using BackgroundAgent;

En el manejador del evento click del botón periódico debemos incluir este código:

private void btnPeriodic_Click(object sender, RoutedEventArgs e)
{
    PeriodicTask tarea = (PeriodicTask)ScheduledActionService.Find("periodica");
    if (tarea == null)
    {
        tarea = new PeriodicTask("periodica");
        tarea.Description = "Tarea periódica";
        tarea.ExpirationTime = DateTime.Now.AddDays(10);
        ScheduledActionService.Add(tarea);
    }

    ScheduledActionService.LaunchForTest("periodica", new TimeSpan(0, 0, 20));
}

En el namespace Microsoft.Phone.Scheduler tenemos la clase SchedulerActionService que nos permite buscar tareas y lanzarlas bajo demanda para probarlas. Es importante no intentar crear dos veces una tarea con el mismo nombre pues el sistema fallará. También debemos tener cuidado con el método LaunchForTest, si intentamos enviar una aplicación al marketplace que use este método será rechazada automáticamente.

Una vez que hemos comprobado que no existe una tarea con el nombre que intentamos usar, podemos crearla, asignarle un nombre y una descripción y por último indicar el tiempo de expiración.

Si ejecutamos la aplicación y presionamos el botón “Tarea periódica” y salimos de la aplicación veremos que en 20 segundos aparecerá la notificación en el sistema:

image

Al pulsar sobre la notificación nos enviará a nuestra aplicación, usando para ello la uri que le hemos proporcionado. Esto es especialmente útil si hemos obtenido datos que deseamos mostrar al usuario, pudiendo enviarle directamente a la página de visualización.

Registrar una tarea intensiva es muy parecido e igual de sencillo. En este caso usaremos el objeto ResourceIntensiveTask:

private void btnIntensive_Click(object sender, RoutedEventArgs e)
{
    ResourceIntensiveTask tarea = (ResourceIntensiveTask)ScheduledActionService.Find("intensiva");
    if (tarea == null)
    {
        tarea = new ResourceIntensiveTask("intensiva");
        tarea.Description = "Tarea intensiva";
        tarea.ExpirationTime = DateTime.Now.AddDays(10);
        ScheduledActionService.Add(tarea);
    }

    ScheduledActionService.LaunchForTest("intensiva", new TimeSpan(0, 0, 20));
}

El resultado de la ejecución de la tarea es el mismo, aparentemente, que en el cado de la tarea recursiva, la diferencia radica en los puntos expresados anteriormente, sobre todo, el mayor tiempo de ejecución de la tarea y las restricciones de uso.

Tanto el objeto PeriodicTask como el ResourceIntensiveTask tienen una propiedad llamada BeginTime, esta viene dada por las clases base de las tareas programadas, pero no es usable en estos dos objetos y producirá una excepción si intentamos establecerla.

Conclusión

Aunque no son perfectas y todavía hay mucho espacio para la mejora, las tareas en segundo plano para Windows Phone 7.5 nos permiten crear nuevas formas de interactuar con el usuario y nos dan nuevas herramientas para realizar nuestras aplicaciones. Todas las restricciones a las que nos vemos sometidos en este sentido como desarrolladores tienen parte de justificación, aunque no del todo, en la comodidad del usuario, que no tenga que pelearse con un gestor de tareas complicado y que no vea afectado su rendimiento sin saber muy bien porqué, o la vida de su batería.

A continuación os dejo el código fuente del proyecto de ejemplo para que lo uséis a vuestro gusto.

Un saludo a todos, espero que os guste y Happy Coding!!