Este documento habla sobre depuración, monitorización y pruebas de software. Describe técnicas de depuración como fijar puntos de ruptura y monitorear variables. También cubre temas como registro de eventos, perfilado de aplicaciones, instrumentación y diferentes tipos de pruebas de software como pruebas blancas y negras.
1. Depuración, monitorización y pruebas Depuración. Depuración es el nombre genérico dado a todas las acciones que acometemos para encontrar los errores en nuestras aplicaciones. Existen distintas técnicas para efectuar la depuración, dependiendo del entorno en el que desarrollemos y ejecutemos los procesos, así como del tipo de acceso permitido a los procesos en ejecución. Aunque los pasos principales se pueden resumir en: Fijamos puntos de ruptura en lugares críticos del código. Monitorizamos las variables importantes para asegurarnos que sus valores son los correctos. Seguimos la secuencia de ejecución del código para validar el flujo y la lógica de negocio. Evidentemente, todas estas opciones sólo serán válidas si podemos disponer de nuestro entorno de desarrollo en los sistemas de explotación, o bien si disponemos de funcionalidades de depuración remota. Ya que, en caso contrario, será imposible efectuar estas acciones, en cuyo caso deberemos utilizar técnicas de seguimiento que nos permitan introducir código para registrar eventos analizables en tiempo de ejecución desde herramientas externas.
2. Depuración, monitorización y pruebas Para ello disponemos de las clases del sistema System.Diagnostics.Trace y System.Diagnostics.Debug que nos permiten monitorizar los procesos mientras se están ejecutando, recolectando y analizando toda aquella información que nos parezca oportuna desde la ventana de output (en depuración), cualquier log, archivo de texto o dispositivo que especifiquemos (en ejecución). Las fases habituales del proceso son: Instrumentación: la adición de los rastreos en el código. Tracing: la ejecución de la aplicación y, por ende, la recolección de la información indicada. Análisis: la evaluación de la información recogida durante la ejecución para identificar y evaluar los problemas.
3. Depuración, monitorización y pruebas Consideraciones para el uso de Trace Cuando estemos escribiendo sentencias Trace o Debug debemos tener presente cómo queremos que se muestre la salida. Escritura de línea completa: WriteLine o WriteLineIf. Escritura parcial de línea: Write o WriteIf. Verificar que se cumpla una condición: Assert. Los switchs del Trace, que van en el archivo de configuración, nos permiten controlar el comportamiento del rastreo en ejecución. BooleanSwitch, activar o no el Trace. TraceSwitch , con varios niveles para indicar que mensajes se emiten. SourceSwitch con varios niveles, lo veremos más adelante.
4. Depuración, monitorización y pruebas Creación de un Switch Para utilizar un Switch para Trace, primero deberemos crearlo en el código y añadir, después, en el archivo de configuración los valores requeridos para el funcionamiento deseado. De esta forma, no será necesario modificar el fuente ni recompilar para cambiar el funcionamiento. [C#] BooleanSwitch dataSwitch = new BooleanSwitch("Data", "DataAccess module"); TraceSwitch generalSwitch = new TraceSwitch("General", "Entire application"); [VB] Dim dataSwitch As New BooleanSwitch ("Data", "DataAccess module") Dim generalSwitch As New TraceSwitch ("General", "Entire application")
5. Depuración, monitorización y pruebas Configuración del Switch Cuando compilamos nuestra aplicación debemos decidir si queremos que las sentencias de Debug y Trace estén o no incluidas, debiendo seleccionar las correspondientes opciones de compilación, en función del tipo de la misma, en la ventana de propiedades, en caso contrario, no se incluirán. Una vez desplegada la aplicación, podremos activar o desactivar la salida, o modificar el comportamiento de la misma, mediante el archivo de configuración y el reinicio de la aplicación o el uso del método Refresh de la clase Trace. <system.diagnostics> <switches> <add name=“dataSwitch" value="0" /> <add name=“generalSwitch" value="0" /> </switches> </system.diagnostics>
6. Depuración, monitorización y pruebas Trace Listeners Los listeners vienen a ser delegados encargados de recibir los mensajes que nuestra aplicación genere y emitirlos hacia el dispositivo de salida correspondiente. Hay listeners definidos para las clases Debug, Trace y TraceSource, todos ellos con la posibilidad de enviar los mensajes a distintos dispositivos de salida. Los predefinidos del sistema son: DefaultTraceListener el cual envía los mensajes hacia los métodos OutputDebugString y Debugger.Log , lo que se traduce en la ventana de salida en Visual Studio. Este es el único que se incluye automáticamente en todas las colecciones de listeners. TextWriterTraceListener envía la salida hacia una instancia de la clase TextWritero cualquier clase de tipo Stream (archivos, consola, …). EventLogTraceListenerredirecciona la salida hacia un log de eventos. ConsoleTraceListenerenvía la salida hacia la salida estándar (Console.Out) o (Console.Error)
7. Depuración, monitorización y pruebas DelimitedListTraceListenerenvía la salida hacia un text writer, como un stream writer o hacia un stream, como un file stream. El formato de salida del trace es el de un texto delimitado. XmlWriterTraceListenerenvía la salida con formato de datos XML hacia un text o un stream writer. Las clases Debug y Trace disponen de una colección de listeners en la que el sistema coloca, por defecto, un DefaultTraceListener, para usar cualquier otro, lo habremos de añadir a esta colección. Todos los listeners recibirán el mismo mensaje y se encargarán de re-enviarlo cada uno al dispositivo especificado. [C#] Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); [VB Trace.Listeners.Add(New TextWriterTraceListener(Console.Out))
8. Depuración, monitorización y pruebas Además de las clases ya vistas, el sistema nos suministra la clase TraceSource, la cual amplía las funcionalidades disponibles con un mayor nivel de detalle, muy útil en las aplicaciones por capas características del entorno .NET. Algunas de las funcionalidades específicas de esta clase son: Se puede utilizar esta clase para diferenciar los orígenes y destinos de los mensajes de Trace. Switches, se usan de una forma similar a la clase Trace, siendo el SourceSwitch el más común. Se dispone del acceso a los mismos listeners que las otras clases. Se pueden añadir filtros a los listeners, con los cuales determinar que eventos se desean procesar,de resultas podremos enviar la salida de cada tipo de evento a un dispositivo diferente. [C#] static TraceSource ts = new TraceSource("TraceTest"); [VB] Private Shared ts As New TraceSource("TraceTest“)
9. Depuración, monitorización y pruebas Recomendaciones practicas: Asegurémonos de que la parametrización de las trazas y depuración son configurables desde fuera del código. Suministremos siempre un método para desactivar totalmente estas funcionalidades. Para grandes proyectos es recomendable el uso de la clase TraceSource en lugar de la Trace, por la funcionalidad de diferenciar orígenes y destinos de los mensajes. Desactivemos la traza y control de errores, sobre todo en aplicaciones en ASP.NET, antes del pase a explotación de las aplicaciones, para evitar revelar información interna a terceros. Incluyamos siempre información complementaria, como la fecha y hora, en los mensajes de traza, para facilitar el seguimiento y análisis. Asegurémonos de que el código asociado a la traza no modifica la información de las variables de la aplicación.
10. Depuración, monitorización y pruebas Perfilado (profiling) de aplicaciones. Se conoce como perfilado o remate de aplicaciones al proceso mediante el cual, una vez completado el desarrollo, rematamos temas de rendimiento, uso de recursos y optimización de código y bucles. Para ello nos centraremos en: Mejorar el uso de memoria de la aplicación. Optimizar aquél código que se usa con mayor frecuencia. Obtener información visual de la aplicación para detectar los puntos de conflicto. Optimizar los bucles. Las herramientas que, a tal fin, nos proporciona Microsoft son: CLRProfiler: descarga gratuita que nos permite perfilar las aplicaciones mediante el acceso al uso de memoria y del GarbageCollector. PerformanceExplorer: parte del IDE de Visual Studio 2008.
11. Depuración, monitorización y pruebas CLR Profiler. Es una herramienta que permite que los desarrolladores tengan una visión del uso que realiza su aplicación de la memoria y del proceso de recolección de basura. Es una herramienta muy útil, que se ejecuta desde fuera del IDE de Visual Studio, como aplicación de ventana o línea de comandos, pero muy pesada, puede hacer que una aplicación corra hasta 100 veces más lenta de lo habitual. La información recogida, de aplicaciones Windows Forms, ASP.NET o servicios de Windows, se almacena en logs que pueden ser posteriormente analizados desde las múltiples vistas de las que dispone la propia herramienta.
12. Depuración, monitorización y pruebas Instrumentación. Son los mecanismos utilizados para generar información sobre el estado del sistema, tanto en cuanto al hardware como al software. Nos da la habilidad de monitorizar o medir el rendimiento de una aplicación y diagnosticar errores, una vez puesta en explotación, lo cual, sobre todo en aplicaciones distribuidas, puede llegar a ser bastante complicado. Pasos: Se debe planificar cuidadosamente la instrumentación en la fase de diseño : qué, cuándo, dónde y porqué deberá ser medido. Habrá que decidir cuáles son los requerimientos de monitorización y soporte. Se deberá establecer una política de instrumentación adecuada.
14. Depuración, monitorización y pruebas Registro de eventos (logging). El componente EventLog nos permite registrar los eventos que se produzcan durante el ciclo de vida de nuestras aplicaciones, para poder visualizarlos y analizarlos mediante la herramienta Event Log Viewer de los sistemas operativos Windows o mediante el Server Explorer de Visual Studio 2008. Podemos utilizar los logs predefinidos del sistema, System, Security y Application, o crear los nuestros propios, con CreateEventSource, para registrar los eventos de las categorías Errors, Warnings, Information, Succes Audits y Failure Audits. Un componente EventLog sólo puede registrar los eventos en un único registro a la vez.
16. Depuración, monitorización y pruebas [C#] if (!EventLog.SourceExists("MyApp1")) EventLog.CreateEventSource("MyApp1", "Application"); EventLog1.Source = "MyApp1"; EventLog1.WriteEntry("Ejemplo de evento registrado."); [VB] If Not EventLog.SourceExists("MyApp1") Then EventLog.CreateEventSource("MyApp1", "Application") End If EventLog1.Source = "MyApp1" EventLog1.WriteEntry("Ejemplo de evento registrado.") Cómo efectuar el registro de eventos (logging). Crear una instancia de registro de eventos: Localizar el registro deseado en Server Explorer. Arrastrar un componente Event Log desde la pestaña componentes de la caja de herramientas. Crear una instancia por código. Para escribir una entrada en un registro de eventos:
17. Depuración, monitorización y pruebas Acceso a los registros de eventos desde código. El usuario bajo el que esté corriendo la aplicación condiciona el acceso que la misma tenga a los registros de eventos: Las cuentas de usuarios de Windows tienen acceso, por defecto, a los registros de eventos. Las cuentas de usuario de ASP.NET no pueden crear categorías, pero si añadir a los registros existentes.
18. Depuración, monitorización y pruebas Pruebas de software. A estas alturas no debería ser necesario recalcar la importancia de las pruebas que hemos de realizar sobre todo el software que desarrollemos, ya que dichas pruebas son cada día más necesarias dada la creciente complejidad de los proyectos que se implantan. Los objetivos de las pruebas de software son: Localizar y solucionar los errores antes del pase a explotación. Verificar que se cumplan los requerimientos del proyecto. Reducir futuros costes a la compañía debidos a errores en el código. Las consecuencias realizar pruebas inadecuadas son: Incremento de los costes para la compañía. Daños en la reputación de la empresa. Vulnerabilidades de seguridad en el software. Retrasos en la entrega del proyecto.
20. Depuración, monitorización y pruebas Tipos de software de prueba. Black-Box: Son los tipos de pruebas en las que se prueban las funcionalidades a ciegas del código desarrollado. Se aplica a todos los niveles del desarrollo, unitario, integración, sistema y aceptación. White-Box: En cambio, se basa precisamente en el conocimiento del desarrollo realizado y la prueba de la estructura interna del mismo. Se aplica en algunos de los niveles del desarrollo: unitario, integración y sistema, aunque lo normal es aplicarlo al nivel de prueba unitaria.
22. Depuración, monitorización y pruebas Buenas prácticas en pruebas de software: No crear dependencias entre diferentes pruebas, que no dependan unas de otras en cuanto al orden de ejecución. Generar y ejecutar código de inicialización de las pruebas, para asegurar un escenario limpio y volver a ejecutar en el caso de error en anterior ejecución. Preparar los ciclos de pruebas antes de iniciar el desarrollo del software propiamente dicho. Se debe crear, al menos, una clase de pruebas para cada clase del código a probar, esto simplifica el proceso y facilita el saber dónde colocar cada prueba. Utilice Visual Studio para generar el proyecto inicial de pruebas, lo cual reducirá significativamente el número de pasos necesarios para prepara el proyecto de pruebas y asociarlo al proyecto de desarrollo. Evítese la creación de pruebas dependientes de rutas concretas y mucho menos a otras máquinas.
23. Depuración, monitorización y pruebas Buenas prácticas en pruebas de software: Se deben crear falsos objetos para las pruebas de interfaces, los cuales nos permitirán validar que las APIs cumplan con los requerimientos. Se debe verificar que todos las pruebas se ejecutan correctamente antes de pasar a la creación de un nuevo ciclo de pruebas, así nos aseguraremos de solucionar los problemas que hayamos descubierto en el código, en el momento en que los hayamos detectado. Debemos generar el máximo número de ciclos de pruebas automáticos posible. Asegurémonos de que no hay posibilidades de ejecución desatendida de pruebas antes de forzar una ejecución manual. Nos aseguraremos de unas pruebas imparciales y que no dependerán del estado de atención del ejecutante.