martes, 9 de diciembre de 2008

Cómo interpretar un burndown chart

En el artículo de hoy veremos cómo podemos interpretar los diferentes tipos de Burndown Charts que nos podemos encontrar en nuestro dia a dia. Como ya hemos visto en artículos anteriores el Sprint Burndown Chart es una herramienta muy útil para saber cómo va la evolución de nuestro proyecto de un solo vistazo. Así pués, es importante saber interpretar la información que nos dá para poder actuar en consecuencia.


Este es un burndown chart ideal. Sinceramente, nunca he visto uno así en la realidad : ). Es a lo que tendria que tender el equipo. Este BC indica que las estimaciones de tiempo se hicieron bien, que el equi

po ha podido trabajar y avanzar cada dia a la velocidad esperada y que a final de sprint se han conseguido realizar todos los puntos de historia planeados. Esto no quiere decir que el equipo no haya tenido impedimentos, pero si que indican que estos havian estado previstos en la estimación inicial, y que estos no han parado a todo el equipo, ya que cada dia se ha visto un avance. También indica que la separación en tareas ha sido buena, pudiendo definir tareas cortas que han significado un avance diario. Esto es lo que nos gustaria llegar a todos y por lo que tenemos que luchar.


Este BC lo que indica es que no se ha podido realizar toda la faena planificada, ni se ha modificado el sprint backlog para intentarlo. Este es un caso extremo ( se realiza un 10% de la faena ) pero es un tipo de BC que se suele dar. Las causas de este tipo de BC pueden ser varias: una mala estimación del sprint ( sobreestimación ), muchos impedimentos a lo largo del sprint, disminución del trabajo del equipo por enfermedad o similar, etc. Lo preocupante de este BC es que nadie ha hecho nada para intentar mejorarlo. Ni se han quitado los impedimentos, ni se ha reducido la carga de trabajo, ni se ha augmentado el equipo de trabajo. Un BC de este tipo tendria que hacer reflexionar tanto al grupo de trabajo ( ¿Qué les pasa a nuestras estimaciones? ) como al Scrum Master y al product owner ( ¿Porqué no he reaccionado a tiempo? ). Claramente es un BC a evitar.

Este BC ya nos gusta un poco más. El inicio del sprint es igual de malo que el anterior, pero a mitad de sprint el equipo ha visto que iba por mal camino. El descenso abrupto a mitad de sprint suele indicar que el product owner ha decidido sacar alguna história del sprint para intentar llegar a final de sprint de la mejor manera posible. También indica que se han eliminado impedimentos, ya que en la segunda parte del sprint el equipo avanza a una mayor velocidad. Es importante este tipo de modificaciones porque un escenario como el anterior deprime al equipo y este se pasa la mitad del sprint sabiendo que no se llegará al objetivo del sprint, cosa que puede hacer que su velocidad decaiga todavía más. Ayudando al equipo a poder cumplir con el objetivo del sprint, lo que ha hecho el product owner ( seguramente orientado por el scrum master ) ha sido mantener el equipo involucrado al 100% en conseguir el objetivo del sprint.

Este BC puede llegar a dar información tan negativa cómo el segundo que hemos visto. Si vemos un BC cómo este no tenemos porque pensar que somos los mejores desarrolladores del mundo, lo que tenemos que pensar es que es que también hemos estimado mal, y es prácticamente tan malo sobreestimar como infraestimar. Es importante que el equipo pueda hacer estimaciones realistas y que se cumplan los objetivos del sprint. De todas maneras, ni que sea por la imagen exterior que se dá, siempre es mejor este tipo de escenario que no un escenario de sobreestimación, ya que este último dá mala imagen del equipo. En el caso que el equipo consiguiera el mismo incremento de valor tanto en el escenario de sobreestimación como en este, la imagen del equipo es mucho mejor en este. Como se puede ver al final el equipo ha decidido añadir tareas de las no planificadas al sprint actual.


lunes, 24 de noviembre de 2008

Insertando una ProgressBar en un DataGridView


Después de unos cuantos posts orientados hacia el terreno de la metodologías y la gestión de proyectos, con este artículo vamos a volver al campo del desarrollo puro y duro.

El objetivo de este post es mostrar cómo poder insertar un control de tipo ProgressBar en una columna del control DataGridView. Este problema, a priori complicado de solucionar ya que el DataGridView no permite la inserción directa de controles en sus columnas, es relativamente sencillo de abordar si se comprende el modelo de objetos que utiliza el control DataGridView de .Net.

No vamos a entrar en detalle sobre el citado modelo de objetos, únicamente explicaremos que para poder crear nuevas columnas (cómo la nuestra de ProgressBar) necesitamos crear una nueva clase que represente el tipo de columna, y una nueva que represente el tipo de celdas que pertenecen a dicha columna. Para ello basta con crear clases que deriven de DataGridViewColumn (o una de sus clases hijas) y DataGridViewCell (o una de sus clases hijas)

Para crear una columna que permita añadir objetos ProgressBar vamos a crear una clase que derive de DataGridViewImageColumn (después veremos porque concretamente de esta y no de la clase base o de alguna de las otras subclases)


public class DataGridViewProgressColumn : DataGridViewImageColumn
{
public DataGridViewProgressColumn()
{
CellTemplate = new DataGridViewProgressCell();
}
}

En esta clase vemos que en el constructor se asigna una propiedad llamada CellTemplate. Esta propiedad se encarga de definir el tipo de celda que corresponde a la columna, de manera que cuando se tengan que crear nuevas celdas el DataGridView sepa cómo crearla. Cómo podemos ver se esta asignado a la propiedad una nueva instancia del tipo DataGridViewProgressCell, que es una clase que también tenemos que crear. Ahora vamos a ver cómo construir la clase que representará a nuestro nuevo tipo de celda:

class DataGridViewProgressCell : DataGridViewImageCell
{
}

Lo primero es derivar del tipo de celda correspondiente (DataGridViewImageCell, que es el tipo de celda que corresponde a la columna de la que habíamos derivado nuestra ProgressColumn)

Ahora vamos a explicar como funcionará esta clase para poder entender todo el código que va a ir dentro de ella:

La celda mostrará una barra de progreso (en el ejemplo la estándar de Windows, pero ya veréis que será muy fácil modificarla al gusto de cada uno) , que irá actualizándose en base al valor que nosotros le pasemos a la celda. Es decir nosotros asignaremos valores enteros entre 0 y 100 y la barra de progreso se irá actualizando. Parece lógico pero ya veréis que si no de deja claro ahora puede ser complicado entender el siguiente código:

Añadimos los siguientes constructores a la clase:

static Image emptyImage;

static DataGridViewProgressCell()
{
emptyImage = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

}

public DataGridViewProgressCell()

{

this.ValueType = typeof(int);
}


En el constructor estático (o de clase) se inicializa una variable de tipo Image mientras que en el contructor público se indica el tipo de datos de la columna, que será un entero. Este valor será el que se utilizará para recoger el porcentaje que se debe mostrar en la barra de progreso y ahora veremos que utilidad tiene la imagen del cosntructor estático.

El siguiente paso es sobreescribir el método GetFormattedValue. Este método sirve para transformar el valor de entrada de la celda (que en este caso es un entero) en el objeto que espera el DataGridView, que al estar derivando de DataGridViewImageCell es una imagen (Ahora se entiende el porqué de la imagen del constructor estático)


protected override object GetFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter,
DataGridViewDataErrorContexts context)
{
return emptyImage;
}



Cómo veis, en lugar de formatear el valor de entrada y devolver la imagen correspondiente que parecía la opción más lógica según lo expuesto arriba en este método devolvemos directamente la imagen vacía, ya que en nuestro caso no se va a utilizar este método para representar el valor del ProgressBar, pero se debe devolver una imagen por consistencia con el tipo de datos de la celda madre (DataGridViewImageCell).

Y finalmente vamos ver cómo tenemos que a sobreescribir el método Paint de la celda. En este método será donde representemos realmente el valor del progreso. Con esto completaremos la clase:

protected override void Paint(System.Drawing.Graphics g, System.Drawing.Rectangle clipBounds, System.Drawing.Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
{
int progressVal;
if (value != null && value.GetType() == typeof(Int32))
progressVal = (int)value;
else
progressVal = 1;

float percentage = ((float)progressVal / 100.0f);

Brush backColorBrush = new SolidBrush(cellStyle.BackColor);

Brush foreColorBrush = new SolidBrush(cellStyle.ForeColor);

base.Paint(g, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, (paintParts & ~DataGridViewPaintParts.ContentForeground));

const int margin = 4;

ProgressBar pb = new ProgressBar();
pb.Height = cellBounds.Bottom - cellBounds.Top - (margin * 2);
pb.Width = cellBounds.Right - cellBounds.Left - (margin * 2);
pb.Value = progressVal;

Bitmap bmp = new Bitmap(pb.Width, pb.Height);

pb.DrawToBitmap(bmp, pb.ClientRectangle);

g.DrawImage(bmp, new Point(cellBounds.X + margin, cellBounds.Y + margin));
}


Lo que estamos haciendo aquí es que cada vez que se llama al evento Paint de la celda se actualiza el estado de la variable ProgressBar interna de la misma y se "pinta" el control dentro de la celda (mediante GDI). Ahora vemos cómo a partir de un entero de entrada tenemos el control ProgressBar en pantalla. El utilizar GDI para "pintar" el control aporta mucha flexibilidad ya que permite darle la apariencia que se quiera: Por ejemplo en lugar de utilizar un control ProgressBar interno y "ver su imagen" podríamos dibujar el resultado del progreso de la manera que mejor nos pareciera, con los colores que mas nos gustasen, con efectos, con texto, etc...

Ahora que ya tenemos las clases preparadas vamos a ver cómo utilizarlas en un DataGridView de manera que podamos visualizar el control en las diferentes filas que se añadan al mismo.

En el ejemplo vamos a suponer que creamos el DataGridView en tiempo de diseño (aunque se podría hacer en tiempo de ejecución). Lo primero es arrastrar el control DataGridView a nuestro formulario y visualizar la pantalla de propiedades del control.


Arrastramos el DataGridView al formulario


Pantalla de propiedades



En esta pantalla definimos las columnas que tendrá nuestro DataGridView. Pulsando el botón Add aparece una pantalla donde se indica entre otras cosas el tipo de la columna y aquí ya podemos ver que en la lista aparece un nuevo tipo, DataGridViewProgressBarColumn, que será el que seleccionaremos para la columna que tiene que mostrar la barra de progreso (Para que el nuevo tipo de columna aparezca hay que tener acceso al espacio de nombres donde hemos definido la clase desde el formulario).



Añadiendo la columna de progreso

Cuando hayamos añadido las columnas necesarias (en el ejemplo añadiremos únicamente dos) volvemos al formulario y creamos un objeto BindingSource arrastrándolo hacia el mismo. Con este objeto implementaremos el enlace entre los datos y el DataGridView.

En la pantalla deberíamos tener algo parecido a esto:


DataGridView con las dos columnas y el objeto BindingSource

El siguiente paso es crear un DataSet que será el que contenga los datos. Este DataSet podríamos extraerlo de una base de datos, de un servicio Web o de cualquier otro origen de datos, en nuestro ejemplo lo construiremos y lo llenaremos programáticamente.

private void InitializeDataSet()
{
//Creamos el DataSet y definimos las columnas.
customDataSet = new DataSet();
customDataSet.Tables.Add(new DataTable("TablaEjemplo"));
customDataSet.Tables["TablaEjemplo"].Columns.Add(new DataColumn("DSNombre", typeof(string)));
customDataSet.Tables["TablaEjemplo"].Columns.Add(new DataColumn("DSProgreso", typeof(int)));

DataRow row1 = customDataSet.Tables[0].NewRow();
row1["DSNombre"] = "Fila 1";
row1["DSProgreso"] = 25;
customDataSet.Tables[0].Rows.Add(row1);

DataRow row2 = customDataSet.Tables[0].NewRow();
row2["DSNombre"] = "Fila 2";
row2["DSProgreso"] = 35;
customDataSet.Tables[0].Rows.Add(row2);

//Ponemos a false el valor AutoGenerateColumns para utilizar las columnas definidas en vista de diseño
dataGridView1.AutoGenerateColumns = false;

//Enlazamos el Dataset con el DataGrid
bindingSource1.DataSource = customDataSet.Tables[0];
dataGridView1.DataSource = bindingSource1.DataSource;
}


Volvemos a la vista de diseño del Grid e indicamos la relación entre las columnas del DataGrid y las del DataSet (opción Columnas de las propiedades del control)


En la opción DataPropertyName de cada columna ponemos el nombre de la columna del DataSet que enlazaremos. Fijaros que son las columnas definidas en customDataSet

Si ahora ejecutamos el proyecto vemos que aparecen las dos filas creadas y que la barra de progreso de cada una corresponde a los valores dados a las columnas del DataSet. Si quisíeramos modificar ese valor sólo tendríamos que ir cambiando el valor de la celda DSProgreso del DataSet y automáticamente se vería reflejado en el Grid y en la barra de progreso.



Gracias al potente modelo de objetos que .Net define para el DataGridView podemos de manera similar al ejemplo mostrado definir columnas para hospedar casi cualquier tipo de control que necesitemos. En ocasiones habrá que derivar de la columna DataGridViewImageCell cómo en el ejemplo, pero podemos cualquiera de las otras clases base según la funcionalidad que necesitemos insertar en nuestra nueva columna.

Os esperamos con vuestras opiniones, sugerencias sobre el tema o críticas al artículo en los comentarios!

Un saludo!!



jueves, 13 de noviembre de 2008

SCRUM ( III )

En el artículo de hoy veremos las diferentes reuniones que se realizan a lo largo de cada uno de los sprints.


Reunión de inicio de sprint

Esta es la reunión de la que nos acordaremos más en el transcurso del sprint sinó la hacemos correctamente. Se realiza al principio de cada sprint, a poder ser un lunes por la mañana, cuando la gente está bien despierta y sin los agobios de toda la semana de trabajo. A ella acuden el dueño del producto, el scrum master y el equipo de desarrollo. La faena del dueño del producto ( como ya hemos visto en anteriores artículos ) es llegar a la reunión con el product backlog priorizado y detallado en sus histórias más prioritarias. En la reunión ( que acostuma a durar toda la mañana ) el dueño del producto explica al equipo cada una de las histórias que cree que van a entrar en el sprint y el entre todos se dedican a dividir la história en las diferentes tareas que la van a componer. Este diálogo es importantísimo ya que ayuda mucho a detallar exactamente lo que se va a implementar en el sprint y evita que al final del mismo haya malentendidos entre el dueño del producto y el equipo de desarrollo. De la misma forma, ayuda a detallar la história tal y cómo la entiende el dueño del producto, ya que el equipo puede creer que para implementar una tarea tiene que hacer un acceso a datos complicadísimo y en cambio el dueño del producto no lo cree necesario. Sin este diálogo, el equipo de desarrollo implementaria este acceso a datos, tardando un montón y haciendo código innecesario ya que el cliente no lo cree necesario.
En esta reunión, una vez detalladas las tareas, el equipo las debe estimar. Hay várias maneras de estimar una tarea, de las quales ya hablaremos algún dia. La estimación también ayuda mucho a refinar las tareas. En la estimación puede quedar reflejado que el equipo de desarrollo cree que para una tarea se debe hacer un algoritmo muy complicado y en cambio el dueño del producto quiere algo más simple. La estimación del equipo saldría con un valor muy alto, valor que sorprendería mucho al dueño del producto y se iniciaria otro diálogo para delimitar el alcance de la tarea.

Cómo podemos ver, aquí queda de manifiesto una de las cosas más importantes de las metodologías ágiles: la comunicación. Es muy importante la comunicación entre los miembros del equipo, desde el dueño del producto hasta los desarrolladores.

La reunión de planificación debe terminar decidiendo entre el equipo y el dueño de producto qué histórias entran en el sprint. Esto lo podemos hacer también de varias maneras ( que también veremos en otro artículo ) pero básicamente de lo que se trata es de estimar la velocidad del equipo en el sprint ( la cantidad de puntos de história que puede hacer el equipo ) y mirar qué histórias de usuario entran en el sprint. Si todavía no conocemos la velocidad del equipo la podemos intentar deducir. Primero asumiremos que un punto de história es el trabajo que puede hacer un desarrollador en un dia de trabajo ( no en un dia ideal, sinó en un dia "normal" ). Esta definición no es importante. Podeis utilizar la definición que queráis. Lo que es importante es que la definición la mantengáis constante a lo largo de la vida del proyecto. No puede ser que un dia utilicemos dias ideales y otro dia dias "normales".

Una vez aclarado esto, sumamos los puntos de história que puede hacer el equipo. Por ejemplo, si somos un equipo de cinco personas, tres de las quales estan a tiempo completo y dos a mitad de jornada, tendremos que en tres semanas el equipo puede asumir 60 puntos de história. A este valor, nosotros le aplicamos otro modificador, para intentar estimar los posibles impedimentos que podamos tener. Un valor normal puede ser un 70%. Por lo tanto, diremos que nuestro equipo es capaz de asumir 42 puntos de historia en un sprint. Teniendo ordenadas las histórias por prioridad, podremos ir viendo qué histórias nos entrarán en el sprint. Por ejemplo, si nuestras histórias hemos estimado que nos ocuparán 6, 10, 8, 12, 4, 8 y 12 puntos de história, decidiremos que haremos las primeras 5 histórias.

Hay dos cosas más importantes que deben decidirse en esta reunión:
  • El objetivo del sprint. Puede parecer superfluo pero és importante definir un objetivo del sprint para que el equipo sepa porqué está trabajando.
  • Definición de acabado. Cómo vimos en el artículo anterior, es vital tener una definición común de acabado entre el dueño del producto y el equipo de desarrollo.

Sprint diário

El sprint diário es la herramienta que nos proporciona scrum para que el equipo pueda compartir conocimiento y se comunique a diário. En esta reunión ( de unos 15 minutos en general cómo máximo ) cada miembro del equipo debe contestar tres preguntas:

  • Qué hice ayer.
  • Qué haré hoy.
  • Qué inpedimentos tengo para realizar mi trabajo.

Esta reunión es muy importante por la visibilidad que dá del proyecto y del trabajo de cada uno. Haciendo asistir a todo el equipo a la reunión evitamos que haya alguien que no haga nada durante un dia. El hecho de tener que decir delante de todo el mundo que no se ha avanzado en nada, hace que la gente trabaje cada día.

Esta reunión también nos sirve para tener el control del avance del proyecto. Gracias al sprint diario podemos actualizar nuestro tablero de scrum ( real o virtual ) y ver cómo avanzan las histórias, ver si tenemos demasiadas histórias en paralelo, demasiadas en testeo, etc. Lo otro que podemos actualizar gracias a la esta reunión es el gráfico de burndown, que nos dá una información muy visual del estado del proyecto y, sobretodo, de la velocidad del equipo, pudiendo ver de un solo vistazo si se llegará a final de sprint con todas las tareas realizadas o no.

Demostración

La penúltima reunión del sprint es la demostración. En la demostración el equipo de desarrollo debe mostrar a todo el mundo los avances realizados en este sprint, es decir, el incremento de valor que han dado al producto. Esto es muy importante porque el equipo se obliga a tener un producto que se pueda ensenyar, en un entorno de preproducción lo más fiable posible. También es importante porque el dueño del producto puede ver si lo que se ha implementado es lo que el pedía, si ha habido algún malentendido, si todo va a la velocidad requerida, etc. A esta reunión también puede asistir otra gente de la empresa, cuya opinión puede ser tenida en cuenta por el dueño del producto para planificar las próximas histórias a realizar. El equipo nunca debe sentirse influenciado por estas opiniones, ya que su interlocutor en qüestión de producto es el dueño del mismo.

Retrospectiva

Es la última reunión de cada sprint. En esta reunión se hace una revisión de cómo ha ido el sprint, qué ha ido bien y qué ha ido mal. Hay várias formas de hacer una retrospectiva. Una de las más habituales y rápidas es hacer una línea temporal en la pizarra e ir apuntando todo lo que se ha hecho en el sprint ( reuniones, impedimentos, logros, etc ). Una vez hecho esto, cada miembro del equipo señala aquellas cosas que le parecen más positivas y aquellas que le parecen más negativas. Las positivas se intentan repetir sprint a sprint. Las negativas se estudian porqué han ido mal y se intenta ver qué se puede hacer para solventar la situación y hacer que vaya mejor en poteriores sprints.


Mini reuniones de diseño

Estas reuniones no son obligatorias pero nosotros recomendamos hacerlas para no sobrecargar en demasía las reuniones de revisión diárias. Su cometido es comentar entre varios miembros del grupo de desarrolladores cosas relativas a decisiones de diseño de la aplicación. Por ejemplo si el miembro A del equipo empieza hoy la tarea "backup de datos de la aplicación" y no tiene claro cómo hacerla, en lugar de empezar una discusión de cómo hacerlo en la reunión de revisión, los miembros implicado se reunen a posteriori para hablar del tema.

Pués esto ha sido todo por hoy. Nos leemos en los comentarios!

martes, 11 de noviembre de 2008

SCRUM (II)

En este segundo artículo sobre Scrum veremos toda aquella "documentación" que generamos a lo largo de la gestión de un proyecto utilizando Scrum. Más allá de la propia documentación del proyecto cómo pueden ser manuales de usuario, archivos de ayuda, diagramas de diseño, etc, hablaremos de la documentación que se genera por el echo de utilizar Scrum. Dado que Scrum es un marco de desarrollo ágil y cómo tal prefiere el software que funciona por encima de la documentación exhaustiva, veremos que la documentación que generamos no es muy amplia aunque, eso sí, muy útil.

Product Backlog El product backlog es el documento donde se describen las histórias de usuario que se quiere que alguna vez esten presentes en nuestra aplicación. Es cómo la lista a los reyes magos. Este documento está mantenido por el dueño del producto, de echo mantener este documento es su principal tarea. Debe mantenerlo actualizado con las histórias que él crea conveninte y, sobretodo, lo debe mantener priorizado. El equipo tiene que saber siempre cuáles son las tareas más importantes para nuestro cliente ( es decir, que tareas tienen un mayor retorno de la inversión ) para así poder centrarse en ellas y dar el máximo valor posible en cada iteración.

Esta lista no tiene porque estar completa al principio del proyecto, de echo, dado que somos un equipo ágil y valoramos el cambio cómo una oportunidad, no nos importa que esta lista vaya variando a lo largo del proyecto. Tampoco hace falta que todos los elementos estén detallados de igual manera; los que tienen que estar más detallados són los que tengan mayor prioridad, los otros ya se irán detallando más adelante. De esta manera gastamos el tiempo del dueño del producto en las tareas más importantes a día de hoy, dejando las menos importantes para más adelante porque quien sabe si al final se van a realizar o se van a modificar.

También es importante acordar la definición de completado ( Definition Of Done - DOD). Sobre este tema, hay un montón de literatura. Cómo muestra os adjunto una imagen sacada del artículo How Do We Know When We Are Done? de lo que entiende el autor por acabado:

Como véis el autor define varios niveles de definición de acabado y es realmente detallista. Es importante tener una buena definición de acabado y que todo el mundo la tenga clara para evitar posibles desviaciones en nuestra estimación por no realizar todas las tareas necesarias.


Sprint Backlog
Es el documento resultante de la reunión de inicio de sprint. En él se detallan lo máximo posible aquellas tareas que se van a realizar en el sprint actual. Se describen con profundidad las histórias de usuario, se dividen en tareas y se estima su esfuerzo. Son estas tareas las que compondran el sprint backlog. Durante el desarrollo del sprint, cada miembro del equipo seleccionará una tarea de entre las más prioritarias cuando acabe de realizar la actual. Esta lista la tendremos que ir manteniendo dia a dia en la reunión de sprint diaria actualizando el estado de las tareas ( no empezada, empezada, acabada ) y actualizando también las horas restantes de desarrollo. Esto nos permitirá mantener los siguiente documentos que pasamos a explicar.

Tablero de scrum
Este tablero nos indica el estado actual de nuestras tareas. Vemos que se compone de una cuadrícula compuesta por filas ( las histórias del product backlog ) y columnas ( el estado de las tareas ). El equipo de desarrollo debe actualizar dicho tablero cada dia en la reunión diária y así todo el mundo puede tener una visión rápida y exacta de cómo se está desarrollando el sprint. También podemos incluir en el tablero las tareas que no estaban planeadas y que se han acabado realizando y las próximas histórias del product backlog, por si acabamos las planeadas inicialmente.
( Imágen extraída del libro Scrum y XP from the trenches de Henrik Kniberg )

Sprint Burndown Chart

En el tablero de sprint también pondremos el sprint burndown chart. Este gráfico nos indica de una manera muy visual cuál es la velocidad del equipo y nos permite vislumbrar si el equipo acabará todas las tareas en el tiempo marcado. Cada día, en la reunión de sprint, el equipo actualizará el tablero de sprint con los datos de avance de estas tareas. Estos datos de avance ( las horas que le quedan a cada tarea ) también se pondrán de manifiesto en el gráfico, haciendo una línea desde el punto de las horas que quedaban por hacer ayer y las que quedan por hacer hoy ( ya que hemos restado las que hemos echo ). Esto, si lo comparamos con la línea ideal que se dibuja desde el punto de máximas horas del dia de inicio de sprint, al punto de horas cero en el úlitimo dia de sprint nos dá información sobre si el equipo avanza a la velocidad adecuada o no. En próximos artículos ya hablaremos de cómo analizar un gráfico de avance.

Y por hoy esto es todo. Nos leemos en el próximo artículo!

martes, 4 de noviembre de 2008

Introducción a las pruebas unitarias (II)

- Frameworks de pruebas unitarias

En este segundo artículo sobre la introducción a las pruebas unitarias vamos ver un ejemplo de aplicación de las mismas.

Cómo norma general las pruebas unitarias utilizan algún tipo de framework que permite su codificación, su ejecución y la visualización de los resultados de los tests. Hay multitud de frameworks donde elegir, por ejemplo NUnit o MbUnit . Para el ejemplo de hoy vamos a elegir el framework de test que viene directamente integrado en Visual Studio (muchos de los existentes pueden integrarse también), que no es otro que MSTest. Este framework está implementado en la librería Microsoft.VisualStudio.QualityTools.UnitTestFramework, que será añadida automáticamente por Visual Studio al proyecto donde se ubiquen las clases de tests.

En el ejemplo vamos a crear los test necesarios para una clase propia que permite realizar operaciones aritméticas básicas (sumar, restar, multiplicar y dividir).

Nuestra clase tendrá el siguiente aspecto:

namespace MathLibrary

{

public class Math

{

public int Sum(int a, int b)

{

return a + b;

}

public int Rest(int a, int b)

{

return a - b;

}

public int Div(int a, int b)

{

return a / b;

}

public int Mul(int a, int b)

{

return a * b;

}

}

}

- Creando el primer test

Una vez creada nuestra sencilla clase vamos a crear las pruebas unitarias necesarias. Para ello hacemos click con el botón derecho sobre el código fuente de nuestra clase y seleccionamos la opción Create Unit Tests…


Seleccionamos la opción de Crear Tests Unitarios…


Aparecerá una pantalla donde podemos indicar varios aspectos relativos a la clase de Tests que vamos a crear:

  • Que funciones queremos testear (cómo normal general las funciones públicas)
  • En que proyecto se debe crear la clase (por defecto aparece la opción de crear un nuevo proyecto)
  • Configuraciones relativas a la nomenclatura de las clases y funciones del proyecto de tests.

Pantalla de creación del test unitario…


Una vez indicados los parámetros de creación de los tests se nos creará la clase (y el proyecto si es necesario) de test. También podremos ver cómo la librería del Framework comentada anteriormente es añadida al proyecto.

En este código hay varias cosas a destacar:

  • El constructor de la clase está decorado con el atributo TestClass. Esto le indica al Framework de Tests que es una clase de testeo.

[TestClass()]

public class MathTest

  • Un método correspondiente a cada uno de los métodos a testear decorado con el atributo TestMethod.

[TestMethod()]

public void SumTest()

[TestMethod()]

public void RestTest()

[TestMethod()]

public void MulTest()

[TestMethod()]

public void DivTest()

  • Una region con 4 métodos que por defecto están comentados

[ClassInitialize()]

public static void MyClassInitialize(TestContext testContext)

[ClassCleanup()]

public static void MyClassCleanup()

[TestInitialize()]

public void MyTestInitialize()

[TestCleanup()]

public void MyTestCleanup()


Cada uno de estos métodos sirven cómo métodos auxiliares para la creación y/o libreación de recursos de los tests unitarios. Cómo alguno habrá deducido estos métodos se ejecutan respectivamente en los siguientes momentos: una vez antes de iniciar la ejecución de los test (MyClassInitialize) , al finalizar la ejecución de todos los tests (MyClassCleanup), antes de iniciar la ejecución de cada uno de los tests (MyTestInitialize) y al finalizar la ejecución de cada uno de los tests (MyTestCleanup).

Estos métodos pueden utilizarse para abrir/cerrar conexiones de bbdd, crear/destruir objetos necesarios para cada ejecución de un test o en cualquier situación otra situación que se crea conveniente.

Hay que comentar que a pesar de que por defecto se crea un método de test por cada método a probar, se pueden crear adicionalmente todos los métodos de test que se crean convenientes para probar el mismo test, por ejemplo para probar diferentes valores de entrada, probar el lanzamiento controlado de excepciones…


¿Cómo implementar las pruebas?


Llegados a este punto vamos a ver cómo probar las funciones de nuestra librería.

Para ello vamos a codificar cada uno de los 4 métodos que nos ha creado visual Studio por defecto.Lo primero es borrar el código que nos ha creado Visual Studio dentro del método (o utilizarlo cómo guía si se cree conveniente), y pensar cómo probar nuestro método. En nuestro caso es bien fácil: Para probar el método de sumar tenemos que comprobar que pasados dos números cómo parámetros nos devuelve un número que corresponde a la suma de los dos. El código del test podría quedar algo así:


[TestMethod()]

public void SumTest()

{

Math math = new Math();

int suma = math.Sum(3, 4);

Assert.AreEqual(suma, 7);

}


Creamos una instancia de la clase Math y comprobamos que el resultado de llamar al método Sum coincide con la suma de los dos números. Para las comprobaciones de los resultados se utiliza la clase Assert, que tiene métodos para realizar comprobaciones de todo tipo. Si la comprobación de dentro del Assert no se cumple, el test lanza una excepción y falla. Si todos los Assert de dentro de un método se cumplen (se pueden poner más de uno), se considera que el método es correcto.


Lo mismo que hemos hecho para la suma lo hacemos para los otros 3 métodos. Ahora sólo queda ejecutar los tests. Para hacerlo utilizamos la barra de herramientas de tests, concretamente la opción de Run All Tests In Solution (también tenemos la posibilidad de ejecutar algunos tests o incluso de debugarlos)




El resultado de los tests se muestra en la siguiente pantalla:



Si alguno de los tests fallase se mostraría el icono correspondiente y el mensaje indicando que error ha sido (si ha sido algún Assert o un error de implemetación en el propio test).


En este punto parece que ya hemos asegurado que nuestra clase funciona correctamente y que los tests cubren todos los casos, pero si nos fijamos un poco podemos ver que el código de la división lanzará una excepción cuando nos pasen cómo segundo parámetro un 0 ¿Cómo se controla esto desde la clase de test?


Pues la idea es declarar explícitamente que se espera que el método lance una excepción en unos casos determinados. En nuestro caso se debería crear un nuevo método que nos testee la división por cero de la siguiente manera:

[TestMethod()]

[ExpectedException(typeof(DivideByZeroException), "División por cero")]

public void DivByZeroTest()

{

Math math = new Math();

int div = math.Div(2, 0);

}


Decorando el método con el atributo ExpectedException aseguramos que el test sea cierto si se lanza la excepción indicada.


La pantalla de resultados de test quedaría de la siguiente manera:



En este punto ya tenemos nuestra clase probada, y nos aseguramos que posteriores modificaciones sobre la misma que produzcan algún error será detectado rápidamente.

-Configuración de tests



Desde Visual Studio se pueden configurar ciertos aspectos de los tests unitarios. Para ello se debe hacer doble click sobre el fichero LocalTestRun.testrunconfig que el IDE generado al crear el test unitario.



Aparecerá la siguiente pantalla:


En la misma podemos configurar una serie de aspectos, cómo el nombre de los tests, los ensamblados a testear, los ficheros a desplegar al hacer los tests, scripts, timeouts, etc…

Podéis investigar por vuestra cuenta con las diferentes opciones de configuración a medida que las vayáis necesitando en la codificación de pruebas.

- Cobertura de código

Una de las opciones a configurar que me parece interesante reseñar es la cobertura de código. Con la cobertura de código podemos saber que porcentaje de nuestro código está cubierto por algún test unitario. OJO, esto no quiere decir que ese código funcione, únicamente indica que hay algún test que se encarga de probarlo!


Para activar la cobertura de código hay que seleccionar la opción correspondiente en la pantalla de configuración y una vez allí marcar el ensamblado que queremos instrumentar (en nuestro caso MathLibrary.dll.

Cuando volvamos a ejecutar nuestros tests podremos consultar la cobertura de código en la pantalla destinada a tal efecto:


Código cubierto al 100%


-Conclusión



Con este artículo cerramos esta pequeña introducción a las pruebas unitarias con Visual Studio. A pesar de la sencillez del ejemplo, debería ser suficiente para empezar a crear vuestras primeras pruebas unitarias.


Y recordad que una de las cosas más importantes de todo equipo de desarrollo es asegurar la calidad del código, y que las pruebas unitarias son uno de los mecanismos más eficaces para conseguirlo!

Un saludo!!



miércoles, 29 de octubre de 2008

Introducción a las pruebas unitarias (I)

Directamente relacionado con los últimos posts de metodologías ágiles, scrum, publicados, en este artículo voy a tratar de explicar de manera sencilla que son las pruebas unitarias y en que nos puede ayudar su adopción. En un segundo artículo veremos un pequeño ejemplo de cómo aplicarlas con ayuda de las herramientas que nos proporciona Visual Studio.

¿Qué son?

Las pruebas unitarias (o Unit Testing cómo encontrareis en la mayoría de documentación existente) no son más que un mecanismo que nos permiten realizar pruebas de manera automatizada sobre las clases y componentes de nuestro código. A pesar de que las pruebas unitarias existen desde hace muchísimo tiempo en el mundo del desarrollo de software, ha sido el auge cada vez más grande de las metodologías ágiles lo que las ha popularizado hasta los límites en que se encuentran hoy en día, al ser una de las técnicas que más adeptos tiene entre los “adoptantes” de dichas metodologías.

También es importante destacar que dentro del contexto de las pruebas unitarias se pueden diferenciar dos corrientes importantes:

· Test-Last Development: O escribir las pruebas una vez realizado el código.

· Test-First Development: Escribir las pruebas antes del código, y a base de refactorización derivar éste último. Ésta corriente es la base del Test Driven Development (o desarrollo guiado por tests).

Cada una tiene sus ventajas e inconvenientes, pero si un equipo está en proceso de adopción de las pruebas unitarias yo, a modo personal, recomendaría una primera aproximación a desde el punto de vista del Test-Last Development.

¿Qué aportan?

Después de esta breve introducción vamos a ver en que nos puede ayudar realizar pruebas automáticas de nuestros desarrollos:

  • Facilidad en la integración de cambios. Gracias a los tests unitarios es inmediato ver si un cambio realizado por nosotros hace que deje de funcionar otra parte del código.
  • Documentación “dinámica” del código (Crece y se actualiza a la misma velocidad que lo hace el código)
  • Facilidad de refactorización. Podemos mejorar, optimizar y simplificar partes de nuestro código con la tranquilidad de saber que si el test que cubría aquella parte sigue siendo válido, el código también lo es.
  • Obligatoriedad de un análisis explícito previo al desarrollo. Para poder realizar las pruebas el desarrollador necesita entender realmente el alcance de la funcionalidad que va a implementar
  • Mejor diseño del software debido al punto anterior
    • Menos acoplamiento
    • Más mantenibilidad
    • Más legibilidad
  • Código más reutilizable. Al tener un mejor diseño de los componentes estos se pueden reutilizar con mayor facilidad haciendo que incremente la productividad del equipo.
  • Detección temprana de bugs. Por lo general cuanto más tarda en detectarse un fallo más impacto tiene en el cliente y mas impacto tiene en la dinámica de trabajo del equipo. Por lo tanto la detección de errores en las primeras fases de desarrollo asegura un menor impacto en el producto y de nuevo una mayor productividad.
  • Reducción del tiempo de depuración.

Todas estas ventajas que nos proporcionan los tests unitarios se pueden resumir en dos grandes mejoras, que, aunque parezcan contrarias, van realmente muy ligadas la una a la otra:

· Mayor velocidad de desarrollo

· Mayor calidad en el producto final

El principal problema para la adopción de las pruebas unitarias en un equipo surge si estas son vistas cómo un coste, y no cómo una inversión.

¿Cómo vamos a aumentar la velocidad de desarrollo si tenemos que perder tiempo en codificar las pruebas unitarias?

Aunque parezca una paradoja la realidad es que la inversión de tiempo realizada en el desarrollo de pruebas unitarias tiene un retorno muy rápido y en muy poco espacio de tiempo veremos cómo la calidad del código desarrollado

En este caso lo que hay que entender es que las pruebas unitarias son una inversión y no un coste, pero además son una inversión con un retorno de la misma casi inmediato y con unas ganancias para el equipo de desarrollo (y para todos los que dependan en mayor o menor medida de el trabajo de éstos) realmente abundantes.

Aparte de todo esto, no se debe perder de vista una máxima crucial en el mundo del desarrollo: la calidad del código nunca es negociable, lo que implica que las pruebas (ya sean unitarias o manuales) nunca deben ser opcionales, y por lo tanto la adopción de pruebas unitarias no debería ponerse en duda si de lo que se trata es de asegurar la calidad de nuestro código.

Y esto es todo, en el próximo post veremos una visión práctica de lo expuesto en este artículo utilizando el Framework de Test integrado en Visual Studio, MSTest.

domingo, 26 de octubre de 2008

SCRUM (I)

Qué es SCRUM

Scrum es un marco de trabajo para el desarrollo ágil de proyectos de software, aunque lo podéis aplicar a cualquier otro ámbito. Si quereis gestionar vuestra vida de pareja con Scrum, adelante!

Scrum, cómo buena metodología ágil y moderna divide el trabajo en iteraciones, a las que llama sprints, que generalmente se alargan entre 2 y 4 semanas. Cada sprint parte de un conjunto de histórias de usuario ( el llamado product backlog ) de las que el equipo de desarrollo con la ayuda del product owner y en la reunión de inicio de sprint escogen aquellas que entraran en el sprint, decidiendo así el sprint backlog. A lo largo del sprint el equipo realiza una reunión diária ( sprint daily meeting ) en la que cada miembro describe el estado de su tarea. Al final del sprint se realiza una demo para mostrar el avance del proyecto en el sprint y después una reunión para evaluar el sprint y su desarrollo ( sprint review meeting ). El objetivo de todo esto es poder entregar un incremento de valor del producto al cliente cada cierto periodo de tiempo de una manera regular.

Si todo esto os suena a chino, no os precupéis que ahora lo vamos detallando un poco.

Los actores de Scrum

Antes de ver cuáles són los diferentes roles en un equipo de Scrum os voy a contar un cuento. Resulta que un dia se encontraron una gallina y un cerdo en el campo. La gallina le dijo al cerdo: He tenido una idea. Vamos a montar un restaurante entre tu y yo. El cerdo le preguntó que cómo se llamaría el restaurante, a lo que la gallina le respondió que huevos con jamón. Cuando lo escuchó el cerdo desestimó la idea alegando que el estaria realmente comprometido con el proyecto, mientras que la gallina sólo estaria involucrada. Si aplicamos esto a Scrum, significa que los desarrolladores són los cerdos ( con perdón :D ) y son los que están realmente comprometidos con el avance del proyecto, ya que son los que lo desarrolan, mientras que la demás gente son gallinas, y aunque de alguna manera u otra están involucrados en el proyecto no estan tan involucrados como los desarrolladores.

Scrum define básicamente tres roles: El equipo, el scrum master ( o facilitador ) y el product owner.

Product owner
O propietario del producto. Es el encargado de mantener el product backlog ( la lista con las historias de usuario ) actualizado y priorizado. Representa al cliente y está presente con voz y voto en la reunión de inicio de sprint para explicar al equipo de trabajo las historias con detalle y llegar a un acuerdo con este sobre las que entran en el sprint backlog.

Equipo de desarrollo
Son los más cerdos de todos ( en el sentido scrumiano de la palabra ). Són los responsables de desarrollar el proyecto. Se reunen con el product owner para aclarar todas las dudas sobre las histórias, estiman lo que van a tardar en hacerlas y se reunen cada dia con el scrum master para controlar el avance del proyecto. Al finalizar el sprint, realizan la demo para mostrar el resultado de su trabajo y se reunen con el scrum master para hacer la evaluación del sprint.

Scrum master
O facilitador. Es el encargado de hacer todo lo posible para que el equipo de desarrollo pueda realizar su faena de la mejor de las maneras. Es su representante en el exterior. Se encarga de eliminar todos los problemas que pueda tener el equipo y de hacer que las reglas de Scrum se cumplan.

El resto de personajes que puedan estar en el proceso son las gallinas de nuestra historia. Pueden asistir a las reuniones pero no tienen voto en ninguna de ellas y voz en muy pocas. Entre estas gallinas podemos encontrar a otros grupos de desarrollos, gente de la empresa interesada en el proyecto, comerciales, etc. Pueden dar su opinión, en ocasiones muy valiosa, pero no pueden interferir en el desarrollo del proyecto.

Y por hoy ya está todo. En próximas entregas veremos la documentación asociada y las diferentes reuniones. Nos leemos!

lunes, 20 de octubre de 2008

Proyectos de instalación con Visual Studio

En el mundo de desarrollo de software empresarial, los instaladores son una pieza de software a la que, por norma general, no se le da la importancia necesaria. De hecho suele ser en lo último en lo que piensa un equipo de desarrollo cuando se empieza un nuevo proyecto, cosa paradójica (y peligrosa) ya que la instalación es la primera toma de contacto que el cliente tiene con el nuevo sistema.

Además hay que tener en cuenta que en los tiempos que corren la mayoría de usuarios a los que va a ir destinado nuestro sistema es gente más o menos familiarizada con los ordenadores y con el software (todo el mundo tiene PC en casa, móviles de última generación, etc.) y un proceso de instalación que sea sencillo y visible aportará confianza y un mayor grado de implicación del usuario en la adopción del nuevo sistema.

Si por el contrario el proceso de instalación es oscuro, difícil y complicado, con multitud de ficheros de configuración ininteligibles y que prácticamente es necesario un master para llevarlo a cabo, los usuarios finales percibirán que están tratando con algo complicado y el rechazo a la implantación del nuevo sistema será mayor.

Todo este rollo de introducción viene a cuento para explicar como realizar instaladores de manera sencilla con Visual Studio. Algo que a mucha gente le parece complicado y tedioso, pero que realmente ,para instaladores no excesivamente complejos, es un proceso bastante simple.

Visual Studio tiene un tipo de proyecto que sirve para este propósito, proyectos llamados Setup Projects (o Proyectos de Instalación en la edición en castellano). Este tipo de proyectos permiten crear diferentes paquetes de instalación para Windows, entre ellos los comúnmente conocidos msi. Para crear un nuevo proyecto de este tipo hay que ir al menú File -> New -> Project y seleccionar el tipo de proyecto Setup Project


Aparecerá una pantalla similar a esta:


En ella podemos distinguir varias zonas:

1) En esta zona de la pantalla aparecen los menús y paneles relativos a la opción del instalador que estemos editando en ese momento. En este caso la opción seleccionada (la que aparece al crear el proyecto) es el sistema de archivos (File System)

2) Barra de herramientas de la pestaña Solution Explorer. En esta barra de herramientas aparecen las diferentes opciones que tenemos para el instalador (por ejemplo el Sistema de Archivos, el editor del Registro, etc.) Hay que tener en cuenta que esta barra irá cambiando según el elemento que tengamos seleccionado en la zona 3 (Solution Explorer). Para visualizar las diferentes opciones del instalador en la barra hay que seleccionar el elemento que representa el proyecto (en nuestro caso SetupExampleProject)

3) Solution Explorer. Representa los elementos que se van añadiendo al proyecto en forma de árbol.

Ahora explicaré las diferentes opciones que Visual Studio nos permite configurar para un proyecto de instalación.

File System Editor

La primera opción que tenemos para personalizar nuestro instalador es el editor del sistema de archivos.



Esta pantalla nos permite crear la estructura de carpetas (lógica) que nuestro instalador creará y utilizará en la máquina de destino y nos permite definir que archivos o componentes contendrán cada una de estas carpetas. Esta estructura se representa en el árbol etiquetado cómo File System on Target Machine en el panel de mas a la izquierda.

Por defecto la estructura cuenta con 3 carpetas, Application Folder, que representa la carpeta donde se instalará la aplicación, User’s Desktop, que representa el escritorio del usuario de la máquina de destino y User’s Program Menú, que representa el menú Programas del usuario. Estas carpetas son representaciones lógicas del sistema de archivos de la máquina destino, y hasta el momento de la instalación no se mapearán a unidades concretas (aunque hay excepciones).

Aparte de las carpetas por defecto se pueden añadir mas carpetas a la estructura, ya sean subcarpetas (por ejemplo subdirectorios de la carpeta de la aplicación) u otros directorios especiales del sistema (por ejemplo la carpeta System32)

Nuevas carpetas añadidas a la estructura de destino


El proceso de añadir una carpeta es tan simple cómo botón derecho sobre la carpeta padre (o la raíz si queremos añadir una carpeta especial) y escoger la opción deseada (la carpeta especial o el nombre de la nueva carpeta según queramos)

Añadiendo una carpeta del SO a nuestro instalador


Una vez creada la estructura es hora de añadir ficheros, ensamblados o accesos directos a nuestro instalador.

El proceso de añadir ficheros o ensamblados es tan simple cómo arrastrarlos a la carpeta deseada (se consigue lo mismo mediante el menú contextual del directorio escogido)

Por ejemplo vamos a añadir a la carpeta de nuestra aplicación un ejecutable para una sencilla aplicación Windows.

Añadimos el exe a la carpeta ApplicationFolder


Añadimos las imágenes a la carpeta de Imágenes creada anteriormente


Ahora podemos crear un acceso directo a nuestra aplicación. Para ello hay que seleccionar el exe que acabamos de añadir y marcamos la opción del menú contextual Create Shortcut to WindowsFormApplication1

Esto nos creará en la misma carpeta un fichero con el nombre Shortcut to WindowsFormApplication1. Podemos cambiarle el nombre por el que queramos, por ejemplo VisorImagenes y lo arrastramos al directorio de la estructura que nos interese. Por ejemplo al Escritorio.


De él acceso directo podemos configurar varias propiedades. Para acceder a la pantalla de propiedades del acceso directo pulsamos Properties en el menú contextual del mismo. La pantalla de propiedades del acceso directo muestra lo siguiente:



Entre otras cosas puede configurarse el icono del acceso directo, los argumentos que se le pasan al ejecutable por línea de comandos, etc.

De la misma manera se pueden configurar distintas propiedades para las carpetas y los archivos.

Por ejemplo, por defecto las carpetas que no son de sistema no se crean en la màquina de destino si están vacias, para evitar esto y forzar a que siempre se creen hay que poner el valor de la propiedad AlwaysCreate de la carpeta en cuestión a True.

La pantalla de propiedades del fichero ejecutable muestra las siguientes propiedades:




Entre otras opciones se puede configurar si se debe registrar el fichero, si es permanente (no se borra al desinstalar la aplicación),etc.

Propiedades de AppFolder

La carpeta de la aplicación tiene una serie de propiedades que vale la pena reseñar:

DefaultLocation : Se muestra la ruta donde se creará la carpeta del instalador. Esta ruta tiene la siguiente forma al inicio-> [ProgramFilesFolder][Manufacturer]\[ProductName]

ProgramFilesFolder indica la carpeta da Archivos de Programa, que puede cambiar de ubicación según el equipo, el sistema operativo, etc.

Manufacturer y ProductName son propiedades genéricas de WindowsInstaller que se pueden definir en las propiedades del proyecto.

Pantalla de propiedades del proyecto


Entre estas propiedades hay muchas importantes para el proceso de instalación, entre ellas las de ProductCode, Upgrade Code o RemovePreviousVersion que se utilizan para actualizar versiones antiguas de la aplicación (podéis encontrar una explicación más detallada aquí -> http://www.simple-talk.com/dotnet/visual-studio/updates-to-setup-projects/ )

Registro de ficheros COM

Visual Studio permite registrar ficheros COM en el proceso de instalación. Las opciones que proporciona el IDE para registrar estas librerías varían en función de su tipo, objeto COM tradicional o Dll .Net exponiendo interfaces y clases COM.



  • Objetos COM tradicionales

Un objeto COM tendrá las siguientes opciones de registro:

    • vsdrfCOMSelfReg : Auto registro. Windows Installer llama a RegSvr32 en el momento de la instalación. No es la opción recomendada.
    • vsdrfCOM: Los datos de registro se crean en el momento de la instalación pero el código de la dll no es llamado en ese punto. Esto asegura que no haya fallos en situaciones donde la dll está linkada a otra dll que todavía no se ha registrado.
    • vsdrfCOMRelativePath: El mismo tipo de registro que el anterior pero en esta opción el Path del objeto es relativo, no absoluto. Se usa en variedades muy específicas de instalaciones.

  • DLL’s .NET

Este tipo de ficheros tienen 2 opciones de registro que corresponden con las dos últimas del punto anterior. Las diferencias entre un tipo y otro se basan básicamente en que los objetos COM tienen la información de registro en la propia dll mientras que las librerías .Net no contienen información del tipo de datos COM. El instalador usa para esto la herramienta tlbExp.exe que genera la información de registro asociada a la librería .Net.

Jugando con las diferentes ideas expuestas anteriormente se puede configurar completamente la estructura de directorios y ficheros que utilizará nuestro instalador.


Registry Editor


Esta sección del instalador permite crear editar o borrar entradas en el registro de Windows. Por ejemplo si queremos crear una entrada en el registro con la localización donde hemos instalado la aplicación, tenemos que ir a la rama del registro deseada y allí crear una nueva clave.


Creando una nueva clave en el registro


Como se ve en la imagen vamos a crear una clave en el registro en la entrada correspondiente a HKEY_CURRENT_USER -> Software -> [Manufacturer]. Este Manufacturer corresponde a la misma propiedad que comentamos brevemente que se puede modificar en las propiedades del proyecto.

Finalmente la el editor de registro quedaria así:



Vemos que hemos añadido una carpeta más a la ruta del registro ([ProductName]) y que después hemos añadido una clave (haciendo botón derecho en el carpeta donde se quiere añadir la clave aparece un menú contextual) a la que le hemos dado el nombre de Location y el valor [TARGETDIR]. Este valor es de nuevo una propiedad del proyecto, que como vemos se pueden utilizar en diferentes sitios del instalador para no tener que establecer los valores en tiempo de compilación. De todas formas también se podía haber optado por colocar una cadena de texto fija (por ejemplo si tuviéramos una ruta de instalación fija y conocida de antemano).

File Types Editor

La sección de Tipos de Archivo permite asociar tipos de archivo a nuestra aplicación. Por ejemplo si nuestra aplicación utiliza unos archivos con extensión “.jkp” podemos definir que se abra automáticamente nuestra aplicación al abrir uno de estos ficheros. No entraremos más en detalle al estar fuera de las funcionalidades más básicas de un instalador.

User Interface Editor


En esta sección se pueden definir las diferentes pantallas de las que constara el instalador. Por ejemplo si habrá una pantalla de bienvenida, de información de licencia al usuario, la de finalización de la instalación, etc.

Hay un número suficiente de formularios de usuario y tiene algunas opciones para personalizarlos, cómo cambiar el banner por defecto, aunque aún está bastante lejos del nivel de personalización de aplicaciones de terceros.

La pantalla muestra dos árboles diferentes, Install y Administrative Install. La primera es la instalación tradicional, donde el programa a instalador está en la máquina a instalar, mientras que la segunda corresponde a una instalación de red, donde hay una fuente desde donde los usuarios de la red pueden instalarse la aplicación.

Un punto a destacar es la instalación silenciosa, o sin interfaz de usuario. No es posible configurar un instalador para que se instale de manera silenciosa, la única manera de hacerlo es a través de la línea de comandos de Windows Installer (msiexec.exe).

Custom Actions Editor


Las acciones personalizadas son la característica de los instaladores de Visual Studio que mas flexibilidad aportan a la generación de instaladores. No entraremos en detalle porque escapan un poco del acercamiento básico de este artículo pero a grandes rasgos lo que permiten es definir una serie de acciones a realizar por el instalador. La forma de definir estas acciones es a través de librerías de código, ejecutables o scripts. Por ejemplo:

· Definir una acción personalizada que nos abra una página de internet que nos interese por cualquier razón.

· Que aparezca un formulario para configurar los parámetros iniciales de una aplicación una vez haya finalizado toda la instalación, o cualquier cosa que se nos ocurra.

· Controlar el tiempo del progreso de la instalación y mostrar el tiempo restante al usuario

Algunas características de las Custom Actions son:

· Se ejecutan SIEMPRE una vez haya finalizado todo el proceso de instalación. Es decir primero se llevan a cabo los pasos definidos en los otros puntos del instalador y finalmente se empiezan a ejecutar las CustomActions, en el orden en que se hayan añadido.

· Se pueden definir acciones para los procesos de instalación, desinstalación, confirmación y rollback (deshacer?). Lo mas normal es que una misma acción tenga implementados todos los casos.

Información más detallada de las Custom Actions en MSDN

Launch Conditions Editor

La última sección proporciona información de las dependencias y condiciones que se han de dar en la maquina de destino para que se lleve a cabo la instalación.

Algunas ya aparecen por defecto, por ejemplo la versión del .Net Framework:



Pero además podemos añadir tanto condiciones de inicio cómo que se haga una búsqueda de un fichero, una clave del registro o un instalador en la máquina de destino, y si estas no se cumplen la instalación no se llevará a cabo.

Con todo esto es posible comenzar a crear instaladores sencillos, que permitan realizar las funciones básicas de un instalador y que además cubren muchos de los casos de las aplicaciones empresariales.

Únicamente faltaría compilar el instalador y si no hay errores proceder a la instalación del mismo. Destacar por último que esta instalación puede realizar de varias maneras:

  • Desde el propio Visual Studio, con un menú contextual que aparece sobre el elemento que representa el proyecto en el explorador de soluciones.
  • Desde el propio msi generado, haciendo doble click en el mismo (o con algunas de las opciones de su menú contextual)
  • Directamente desde la línea de comandos con la utilidad msiexec.exe.

Para obtener más información y funcionalidades avanzadas de los proyectos de instalación con Visual Studio podéis consultar el siguiente enlace : http://msdn.microsoft.com/en-us/library/aa372845(VS.85).aspx

También interesante es el siguiente blog donde se muestran una serie de artículos muy interesantes sobre los proyectos de instalación de Visual Studio: http://www.simple-talk.com/dotnet/visual-studio


Esperamos vuestros comentarios con impaciencia!