Home RSS 2.0 ATOM 1.0  CDF  
 
CodeSegment - Carlos Segura Sanz (blog)
 
Page 1 of 4 in the NET Development category Next Page

El miércoles día 17 daré una ponencia sobre “Testing, reducción de costes de no calidad” en los cursos de veranos organizados por la Universidad Pública de de Navarra, el curso se llama “Retorno de la inversión de la calidad del software en las pymes”.

Una de las preocupaciones que tienen las organizaciones que desarrollan software es mejorar sus resultados financieros apoyándose en la mejora de los procesos de desarrollo siguiendo modelos de buenas prácticas como por ejemplo CMMI. Estas empresas se involucran en iniciativas de mejora estimando que van a conseguir una mejora suficiente, pero sin conocer a priori el retorno de inversión.

En este curso se presenta la metodología ROI para evaluar las iniciativas de mejora en las que las empresas se involucran para mejorar sus procesos de desarrollo.

A su vez, se cuenta con la presentación de experiencias de empresas involucradas actualmente en iniciativas de mejora y empresas acreditadas en el modelo CMMI. Finalmente, se presenta ejemplos prácticos para conseguir mejorar el retorno de inversión en las organizaciones que desarrollan o que contratan el desarrollo, por actividades de testing.

El programa del curso podéis descargarlo desde aquí

Monday, September 15, 2008 4:24:37 PM (Hora de verano romance, UTC+02:00)   #    Comments [0]   Misc | NET Development  | 

Dentro del proceso de integración continua que uso, los proyectos que no tienen un archivo FxCop, los proceso usando una plantilla a la que añado mediante una tarea en el MsBuild, los targets necesarios para después poder ver si hay algo muy cantoso …

La cosa es que en el resto de proyectos mantengo un archivo FxCop, en el que voy excluyendo algunas de esas cosas cantosas tras revisar el código.  El problema viene cuando en el build server, se ejecuta el FxCop, y genera el informe, ya que en este informe aparecen todas las incidencias, incluidas las que yo he excluido. (grrr)

De modo que he decidido acabar de una vez con el problemilla.. lo que hago ahora es procesar el informe de FxCop con el XSL (que os dejo abajo) para que cuando el CC, haga el merge, solo estén las activas.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:output method="xml" indent="yes"/>
 
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
 
  <xsl:template match="//Message">
    <xsl:if test="self::node()/@Status='Active'">
      <xsl:copy-of select="self::node()" />
    </xsl:if>
  </xsl:template>
 
</xsl:stylesheet>


Para procesar el XML, podéis usar la tarea XSLT del Community.Tasks, para MSBuild.

<Exec Command="$(FxCopExe) /project:$(FxCopProject)/out:$(ArtifactPath)\$(FxCopReport)temp" />          
<Xslt Inputs="$(ArtifactPath)\$(FxCopReport)temp"        
      Xsl="$(FxCopActive)"
      Output="$(ArtifactPath)\$(FxCopReport)" />



Y listo ..
Thursday, March 27, 2008 4:02:48 PM (Hora estándar romance, UTC+01:00)   #    Comments [0]   NET Development  | 

James W. Newkirk, uno de los co-autores de NUnit, trabaja desde el año pasado junto con Brad Wilson en un framework para realizar "pruebas" llamado xUnit.

Lo he seguido con cariño y finalmente lo he adoptado para un nuevo proyecto en el que estoy trabajando, dejando mi querido MbUnit.

El cambio de NUnit a MbUnit, lo hice debido a que este último incorporaba RowTests, posibilitando el ejecutar un mismo test usando distintos valores, lo cual acortaba el número de pruebas a escribir sustancialmente.

xUnit va más allá, según explica James, entre las lecciones aprendidas con NUnit, una de las más importantes fue el hecho de mantener el código de las pruebas tan simple como fuera posible, eliminando las duplicidades.

Se han eliminado atributos como [TestFixture] para indicar que se trata de una clase que contiene tests, y el [Setup] y [TearDown] quedan enmascarados en el código de la clase, de manera que el atributo [Setup] equivaldría al  constructor y si la clase implementa IDisposable el método Dispose hará las funciones del atributo [TearDown].

Obviare aquí la discusión sobre el uso de estos dos atributos…  Más, [TestFixtureSetup] y [TestFixtureTearDown] también desaparecen, como alternativa, nuestra clase pude implementar el interface ITestFixture.

Otro de los atributos que desaparece es [ExpectedException] volviendo al clásico estilo de JUnit aprovechando los delegados anónimos de c# 2.0, esto como sabéis podía llevar a error en algunas de las pruebas debido a que otro método distinto al probado lanzara la excepción. La lectura del test no deja lugar a dudas sobre lo que se está probando.

Assert.Throw(typeof(ArgumentNullException), delegate { clase.Metodo(null); });

ó

Assert.Throw<ArgumentNullException>(delegate { clase.Metodo(null); });


Desaparece también [Test], que pasa a llamarse [Fact], este pequeño matiz  puede desconcertar, pero el HECHO, es que viene de la propia naturaleza de xUnit que tiene el propósito de ser usado más como herramienta para el desarrollo dirigido por pruebas a ser otra de las herramientas de pruebas. Si no dando por HECHO un comportamiento que debe cumplirse.

En cuanto a las aserciones, puede que sean un conjunto más reducido (o priori menos expresivo) que en otros frameworks, pero hace un uso intenso de los genéricos dotándoles de la expresividad necesaria para que la lectura de las mismas no sea un problema. A mi desde luego me encanta.

Hasta aquí xUnit, no es más que otro framework de pruebas con las peculiaridades que hemos visto, sencillo y simple.

Y de esa naturaleza, surgen las Extensiones (más para el que quiera más)  Y YO QUERIA MÁS.

Las extensiones de xUnit, son una pasada. Añaden una serie de atributos al framework que para mí las hacen indispensables.

El atributo [Theory], nos permite hacer pruebas dirigidas por datos, similar en parte al atributo [RowTest] de MbUnit. Los datos pueden escribirse en línea [InlineData], venir de una hoja de cálculo [ExcelData], de una fuente OleDb [OleDbData] o de Sql server [SqlServerData], y usando [PropertyData] podremos hacer que los datos provengan de una propiedad de nuestra clase. (yo creo que ya no puedo vivir sin esto)

Otro de los atributos incluidos en las extensiones es [AutoRollback], que es exactamente igual al [RollBack] de MbUnit, mete todo en una transacción y deshace los cambios al terminar la prueba, con lo cual la base de datos se queda como estaba.

El atributo, [FreezeClock], nos permitirá bloquear el reloj para poder realizar pruebas que utilizan la hora del sistema. No lo he probado pero se me ocurren algunas cosas interesantes a probar con WF. 

Con [AssumeIdentity] podemos cambiar la identidad del usuario que está ejecutando el ensamblado (con esto estoy jugando … )

Y por último un par más como [Trace] que añade una entrada en DebugView o cualquier otro listener, y [RunWithNUnit] que nos permitirá ejecutar pruebas también con NUnit.

Todavía tengo que probar algunas de las cosas que trae, pero por el momento se queda. Además incluye un plugin para el Resharpher.

Más:

http://www.codeplex.com/xunit
http://www.codeplex.com/xunitext/Release/ProjectReleases.aspx?ReleaseId=8103


James ha escrito tres libros, yo tengo un par Test-Driven Development in Microsoft .NET (Microsoft Professional) y Extreme Programming in Practice, recomiendo el primero.

Y el año pasado publico Visual Studio Team System: Better Software Development for Agile Teams (Microsoft .Net Development).

Wednesday, February 06, 2008 3:55:48 PM (Hora estándar romance, UTC+01:00)   #    Comments [2]   NET Development  | 

Llevas una hora caminando con una piedra en el zapato y finalmente la sacas cuando ya no puedes más, entonces dices .. buff que alivio .. ¿Por qué no habré sacado la piedra antes?

Una piedra, unas llaves en el bolsillo de atrás, dejar la mochila cuando estas parado hablando con alguien por 30 minutos… son innumerables  la cantidad de ejemplos que podíamos citar aquí.

La cuestión es que las el paso de una pequeña molestia a una gran molestia y de una gran molestia a decidirnos resolver el problema un tanto incierto, y con casi toda seguridad intervienen un montón de factores en cada situación.

Dentro de esas gran cantidad de ejemplos, y como no ciñéndonos a la programación, cuantas veces os han pasado cosas similares, compilas y ves algo que no está bien, pero no es urgente .. Sigues, una y otra vez, hasta que tomas la decisión de erradicarlo por completo para no verlo más, no porque sea el momento de hacerlo si no, porque ya molesta, vamos nos está tocando la moral. Vuelves a compilar y bufff que alivio, es como la dichosa piedra del zapato.

Hay que ver qué tontería (por que estas cosas suelen ser tonterías que generalmente no cuestan nada solucionar) pero pienso que tenemos en la cabeza otro problema, algo que es más urgente de programar o hacer y que es realmente lo importante, hasta el punto de que mientras la piedra ó el errorcillo tonto, no sea capaz de desconcentrarnos de lo que estamos haciendo, no tienen cabida resolverlo.

La cosa esta en cuál sería la mejor manera de codificar algo similar, evidentemente no sería un problema usando threads, uno resolviendo el problema principal y el otro comprobando si alguien te toca los Eggs hasta el umbral necesario para interrumpir el proceso principal y resolver el problema.  ¿Se podría hacer sin usar hilos?

Todo esto en principio era para poner un pequeño script con el que he resuelto una piedrilla de esas,  en fin, me he puesto filosófico y …

La cosa es que cuando en el proceso de generación que uso para mis aplicaciones de SharePoint, siempre cuando cambio el modo entre Debug y Release, tengo que andar tocando uno de los archivos de configuración e indicarle de donde ha de coger la DLL que irá en el archivo de instalación. (Esa era mi particular piedrilla)

Evidentemente ya se me había pasado por la cabeza incluir en la pre-compilación  el invocar algún tipo de utilidad que hiciera el cambio de rutas pertinente, pero siempre he descartado esto porque no me parecía muy bien incluir la susodicha utilidad dentro del proyecto y como tal que esta fuera a parar al repositorio.

Sin embargo hoy me ha venido a la cabeza el usar un pequeño script de visual basic script que realice el cambio.

De modo que en la pre-compilación podemos pasarle una plantilla del archivo, en esta plantilla indicamos una serie de variables que serán reemplazadas.

cd "$(ProjectDir)"
cscript tools\replace.vbs "$(ProjectDir)template.ddf" "$(ProjectDir)makecab.ddf" "[DIR]" "$(OutDir)"

Reemplazará [DIR] por el directorio en donde estamos generando la DLL

El script tontorrón ...

strFileIn = Wscript.Arguments(0)
strFileOut = Wscript.Arguments(1)
strOldText = Wscript.Arguments(2)
strNewText = Wscript.Arguments(3)
 
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(strFileIn, 1)
 
strText = objFile.ReadAll
objFile.Close
strNewText = Replace(strText, strOldText, strNewText)
 
Set objFile = objFSO.OpenTextFile(strFileOut, 2, True)
 
objFile.WriteLine strNewText
objFile.Close


Monday, January 28, 2008 1:14:54 AM (Hora estándar romance, UTC+01:00)   #    Comments [1]   NET Development  | 

Mi buen amigo Rodrigo me dice: "A ver si jubilas NUnit y te pasas a Team System". Bien, ese es mi propósito para el año entrante.

Jubilar NUnit, CruiseControl, RedMine y mi querido Subversion. En cuanto salga Windows Server 2008, y Team System 2008 será lo primero que haga, remodelar mi pequeña y barata infraestructura.

Como decía actualmente estoy usando lo siguiente en un modesto Pentium 4, con 512Mb de Ram que hace las labores de mini-servidor en casa.

Pruebas unitarias: MbUnit y NUnit
Integración Continua: CruiseControl.Net y CruiseControl.rb
Cobertura de código: NCover
Construcción: Nant
Repositorio de código: Subversion
Gestión de proyectos: RedMine

Una infraestructura de coste 0, versátil y potente, que me permite hacer prácticamente de todo.

Si, tal vez no esté todo integrado ni disponga de un entorno unificado, pero no se le ha resistido prácticamente nada. Herramientas con las que he pasado horas y horas de esas noches desveladas en que el proceso de automatizar tareas se convierte casi en  una experiencia religiosa, “ala Enrique Iglesias” si, descubriendo el infinito… herramientas que han dejado el listón muy arriba, que por el momento me hacen dudar si TFS estará a la altura, si llegará a cautivarme como lo han hecho mis queridas compañeras…

Incluida la última en llegar que ha sido RedMine, para aquellos que no la conozcan, es un gestor de proyectos, escrito en Ruby por Jean-Philippe Lang y es uno de los proyectos más activos dentro de la comunidad de Ruby.  

Con RedMine, se pueden gestionar diversos proyectos de toda índole, aunque está basado en Ruby, yo por ejemplo lo uso para gestionar proyectos  .Net, permite que varias personas trabajen en el mismo equipo, planificar objetivos, seguimiento de incidencias, gestión de la documentación del proyecto mediante Wiki, Foros, integración con distintos repositorios de datos (Subversion, CVS, Mercurial, Bazaar y Dracs), autentificación por LDAP etc…

Yo incluso lo tengo personalizado (lo bueno de poder tocar el código) para que me muestre en la página principal de los proyectos información sobre la últimas compilaciones, información que proviene del CruiseControl.




También, modifique el visor del repositorio para poder mostrar código c# con la sintaxis coloreada, para que gestionar documentación sobre los frameworks, ver informes sobre cobertura etc... en fin, pueden ser pijadas para algunos, en fin echaré de menos todas esas horas editando archivos XML, encadenando procesos y generando informes…   

Prometo contar mis experiencias religiosas con TFS el año que viene…
Thursday, December 13, 2007 1:30:23 PM (Hora estándar romance, UTC+01:00)   #    Comments [5]   NET Development  | 

Alguien me ha recordado que empecé a escribir sobre el tema de las pruebas unitarias y finalmente no termine… o por lo menos no conté algunas de las prácticas que uso… cierto.

Parte 1 - Parte 2

Básicamente la técnica que uso habitualmente consiste en aprovisionar un sitio de forma automática. Y ¿Cómo se aprovisiona un sitio automáticamente?, pues de la manera más sencilla posible. Usando Plantillas.

Provisión - Escenario de Pruebas

De modo que tengo distintas plantillas pre cargadas con listas, bibliotecas de documentos, páginas etc… algunas incluyendo contenido, archivos, elementos de las listas y un montón de cosas.

Es sencillísimo crear estos escenarios y pueden ser simples, incluyendo uno ó unos pocos elementos ó complejos incluyendo una jerarquía de de sitios, en cuyo caso crearemos una plantilla por cada sitio para más tarde reconstruir una jerarquía de sitios determinada.

Después usamos un ayudante en nuestras pruebas para aprovisionar nuestros sitios, un ejemplo de un aprovisionador de sitios  básico sería algo como esto, he recortado algunas cosas ...

public class SPProvisioningHelper
{
private const string DefaultWebName = "TestSite";
private readonly Dictionary<string, SPWeb> _webs;

private SPSite _site;
private string _urlTestSite;

public SPProvisioningHelper()
{
_webs = new Dictionary<string, SPWeb>();
UrlTestSite = "http://enjendro/unittests/";
}

// Url del sitio de pruebas public string UrlTestSite
{
get { return _urlTestSite; }
set { _urlTestSite = value; }
}

// Coleccion de sitios sobre la que estamos trabajando public SPSite Site
{
get { return _site; }
}

// Una web determianda public SPWeb GetWeb(string name)
{
return _webs[name];
}

// Provisiona una web vacia colgada de parentUrl // Pruebas rápidas y de creación de elementos public void WebProvisioning(string parentUrl)
{
_webs.Add(DefaultWebName, CreateWeb(DefaultWebName, parentUrl, string.Empty));
}

// Crea una web public void WebProvisioning(string name, string parentUrl)
{
_webs.Add(name, CreateWeb(name, parentUrl, string.Empty));
}

// Crea una web basada en una plantilla public void WebProvisioning(string name, string parentUrl, string template)
{
_webs.Add(name, CreateWeb(name, parentUrl, template));
}

// Crea las webs (hace el trabajo duro) private SPWeb CreateWeb(string title, string parentUrl, string template)
{
Trace.WriteLine(string.Format("Creating test web {0} at {1}", title, parentUrl));

_site = new SPSite(UrlTestSite + parentUrl);
SPWeb web = _site.OpenWeb();
SPWeb newWeb;

// Stop if (web == null)
{
throw new SPException(string.Format("Can´t open SharePoint Site in: {0}", _site.Url));
}

// Si existe lo borramos if (web.Webs[title].Exists)
{
RecursiveDeleteWebs(web.Webs[title]);
web.Webs[title].Delete();
}

// Web vacio if (template.Equals(string.Empty))
{
newWeb = web.Webs.Add(title);
}
else { // web basado en plantilla personalizada try
{
SPWebTemplateCollection webTemplateCollection;
webTemplateCollection = _site.GetCustomWebTemplates((uint) web.Locale.LCID);

SPWebTemplate webTemplate = webTemplateCollection[template];

newWeb = web.Webs.Add(title,
title,
string.Empty,
(uint) web.Locale.LCID,
webTemplate,
true,
false);
}
// web basado en plantilla no personalizada catch (ArgumentException)
{
newWeb = web.Webs.Add(title,
title,
string.Empty,
(uint) web.Locale.LCID,
template,
true,
false);
}
}

Trace.WriteLine(string.Format("Created web {0} at {1}", newWeb.Title, newWeb.Url));

return newWeb;
}

// Limpieza public void Clean()
{
DeleteWebs();
// Disposes } // Elimina las webs private void DeleteWebs()
{
foreach (SPWeb web in _webs.Values)
{
RecursiveDeleteWebs(web);
}
}

// Elimina todo recursivamente private void RecursiveDeleteWebs(SPWeb parentWeb)
{
// ... } }

Pruebas

Después en nuestras pruebas podemos inicializar los distintos escenarios, veamos un ejemplo usando el aprovisionador anterior

[TestFixture]
public class Tests
{
// El ayudante private SPProvisioningHelper provisioningHelper;

[TestFixtureSetUp]
public void Setup()
{
// Incializamos el ayudante provisioningHelper = new SPProvisioningHelper();

// Ejemplos // Aprovisionamos p1 desde una plantilla nuestra provisioningHelper.WebProvisioning("p1", "P1", "Plantilla1.stp");

// jerarquia provisioningHelper.WebProvisioning("p1", string.Empty);

// p1/p1a -> sitio de grupo provisioningHelper.WebProvisioning("p1a", "p1", "STS");

// p1/p1b -> un blog provisioningHelper.WebProvisioning("p1b", "p1", "Blog");

// p1/p1a/p1a1 -> un wiki provisioningHelper.WebProvisioning("p1a1", "p1/p1a", "Wiki");

// p1/p1a/p1a1/p1a11 -> sitio en blanco provisioningHelper.WebProvisioning("p1a11", "p1/p1a/p1a1");

}

// Una prueba simple [Test] public void GetCurrentUserName()
{
string expectedUser = WindowsIdentity.GetCurrent().Name;

SPUser user = provisioningHelper.GetWeb("p1").CurrentUser;

string userName = user.LoginName;

Trace.WriteLine(string.Format("Expected: {0} User: {1}", expectedUser, userName));
Assert.IsTrue(expectedUser.Equals(userName,StringComparison.OrdinalIgnoreCase));
}

// Limpieza [TestFixtureTearDown] public void TearDown()
{
provisioningHelper.Clean();
}
}

Esto es básicamente, desde luego como ocurre con todos los sistemas que deben ser aprovisionados, estos llevan su tiempo, en cualquier caso tampoco me parece tanto y además hay unos estupendos quad core relativamente baratos así que no es escusa para no tener una máquina de pruebas.

Wednesday, December 12, 2007 10:44:44 PM (Hora estándar romance, UTC+01:00)   #    Comments [0]   NET Development | SharePoint-es  | 

Y el séptimo día, descansó

¿Pero? ¿Y si hubiera tenido que seguir un día tras otro y una semana tras otra? Más o menos lo que nos pasa a los desarrolladores, el trabajo nunca termina, con una línea completas un método, con un método una clase, con una clase un caso de uso un espacio de nombres, una biblioteca… siempre habrá detrás otra nueva clase, otra nueva biblioteca y así sin parar.

Un día tras otro…  codificando, depurando, compilando, probando, no pongo meando porque a veces creo que me lo puedo hacer encima por no despegar los dedos del teclado …

Y a todo esto, cientos y cientos de tecnologías a nuestro alrededor que cambian constantemente, ¿y si a Dios le hubiesen cambiado las especificaciones una y otra vez?, y ¿si cada día hubiera tenido una nueva tecnología a su alcance?

Sinceramente, el mundo estaría por hacer. Sólo con el tiempo que lleva aprender, probar, crear un manual de buenas prácticas y realizar un framework adaptable y extensible y bla bla bla, para poder empezar a trabajar… estaría todavía diseñando a Adán.

Y eso gracias a que no dejo que Adán  diera las especificaciones de Eva, porque todavía estaríamos esperando… (Lo digo sin ánimo de ser machista), porque estaríamos  todavía en alguna iteración de la metodología divina, comprobando si el modelo cumple con las especificaciones del cliente.

En fin, si las cosas fueron así de rápidas supongo que serian por que no había, “cliente” y ¿si no había cliente?, ¿si no había especificaciones?, siempre me quedaré con la duda si se pudo hacer  en 5 ó incluso en 4 días y se demoró el asunto. Digo yo, que siendo Dios, podía haberlo hecho todo en UNO, Mundo y Ploffff, toma MUNDO.

De modo que no me queda más que pensar que uso algún tipo de metodología para acometer el trabajo. ¿Cuál?, ¿Cómo hacer un mundo en 6 días y descansar el 7º? ó tal vez usó ¿Cómo hacer el mundo en 4 días y descansar 3?, desde luego que si uso esta no consiguió su objetivo, a pesar de la metodología.

Creo que nos quedaremos sin saberlo…

Volviendo al asunto, que en absoluto es la metodología, si no la gran cantidad de tecnologías de las que disponemos, y la gran cantidad de las que dispondremos en breve.  Haciendo un pequeño resumen de las que necesitamos para desarrollar, pongamos por ejemplo SharePoint… un lenguaje .Net(C# ó VB.Net), Html, ASP.Net, XML, XSLT, JavaScript, Windows Workflow Foundation, InfoPath, TSQL, un SDK gordito y sin ejemplos, con más de 120Mb, 98 NameSpaces y 1.256 clases…

¿Sabéis una cosa? Yo quiero, que llegue el 7º Día.

Tuesday, September 25, 2007 8:03:13 PM (Hora de verano romance, UTC+02:00)   #    Comments [2]   Misc | NET Development | SharePoint-es  | 

Como comentaba hace unos días, realizar pruebas unitarias usando mocks, no se siempre la mejor solución, personalmente, creo que no se debe abusar de ellos y usarlos para todo.

A continuación os cuento, como realizo mis pruebas unitarias cuando programo para SharePoint, seguramente no sean lo más idóneo del mundo, pero como dije en su día, no considero esto una ciencia sino más bien, un arte en donde cada uno utiliza aquello con lo que obtiene los mejores resultados, en cuanto a calidad. Siempre mantengo en mente el principio de que no deben llevar más trabajo que la realización del código.

El SDK de SharePoint, como comenté con anterioridad, es un framework con lo cual debemos dar por hecho algunas cosas, tenemos que tener claro que Microsoft, tomo ciertas decisiones en su diseño que por ende, nosotros no podemos cambiar.

SharePoint consta de un complejo modelo de objetos, en tres capas. De modo que cada vez que manipulamos estos, de forma transparente vamos cambiando la base de datos donde estos son persistidos. Esta cuestión es importante ya que es como si trabajásemos contra una base de datos y todos los que han realizado pruebas unitarias con bases de datos, saben la complejidad añadida que esto conlleva. Al margen de la discusión ya conocida sobre si las pruebas unitarias deben o no deben usar la base de datos como apunto Rodrigo.

Yo soy de la opinión de que mientras se pueda (por que se conoce) que base de datos se va a usar y podemos recrear un modelo de los datos sin un gran esfuerzo, es la mejor opción.

En SharePoint, aplico el mismo principio. Por diseño, la base de datos esta de manera subyacente así que la mejor manera de probar las cosas es usándola.

Mis pruebas con las Webparts

Las Webparts son un elemento dentro de SharePoint que nos permite interactuar con el usuario, a través del interface de usuario. En esta nueva versión como sabéis son los nativos de ASP.Net.

Como tales elementos, debemos confiar en que el comportamiento que tienen es el esperado, y me centro en realizar pruebas de la lógica subyacente, aquello que se encargará de producir los resultados en función de las entradas que reciba.

Veamos un simple ejemplo con un webpart que muestra el número de elementos que cumplen una condición en una lista.

private string _list = string.Empty;
private string _message = string.Empty;
private string _query = string.Empty;

#region WEBPART PROPERTIES
[Personalizable(PersonalizationScope.Shared),
 WebBrowsable,
 Category("Settings"),
 WebDisplayName("List Name")]
public string List
{
...
}

[Personalizable(PersonalizationScope.Shared),
 WebBrowsable,
 Category("Settings"),
 WebDisplayName("CAML Query")]
public string Query
{
...
}

[Personalizable(PersonalizationScope.Shared),
 WebBrowsable,
 Category("Settings"),
 WebDisplayName("Mesaage")]
public string Message
{
...
}
#endregion

protected override void Render(HtmlTextWriter output)
{            
    try
    {
        SPWeb web = SPControl.GetContextWeb(Context);
        SPList list = web.Lists[List];
        SPQuery query = new SPQuery();
        query.Query = Query;
        SPListItemCollection items = list.GetItems(query);
        output.Write("<div>{0}<b>{1}</b></div>", Message, CountListItems(web, List, Query));
    }
    catch (Exception ex)
    {
        output.Write(String.Format("<div>{0}: {1}</div>", ex.GetType(), ex.Message));
    }
}

Bien, aquí algunas buenas prácticas que suelo recomendar. Siempre que podamos debemos independizar nuestra lógica del componente, esto lo podemos hacer bien separando nuestro código en nuevos métodos:

protected override void Render(HtmlTextWriter output)
{
    SPWeb web = SPControl.GetContextWeb(Context);
    try
    {
        output.Write("<div>{0}<b>{1}</b></div>", Message, CountListItems(web, List, Query));
    }
    catch (Exception ex)
    {
        output.Write(String.Format("<div>{0}: {1}</div>", ex.GetType(), ex.Message));
    }
}

private int CountListItems(SPWeb web, string listName, string queryFiler)
{
    SPList list = web.Lists[listName];
    SPQuery query = new SPQuery();
    query.Query = queryFiler;
    SPListItemCollection items = list.GetItems(query);

    return items.Count;
}

O lo que es mejor, aplicando los principios de delegación o composición para extraer las responsabilidades y la lógica a una nueva clase.

protected override void Render(HtmlTextWriter output)
{
    SPWeb web = SPControl.GetContextWeb(Context);
    ListItemCounter listCounter = new ListItemCounter(web);

    try
    {
        output.Write("<div>{0}<b>{1}</b></div>", Message, listCounter.CountListItems(List,Query));
    }
    catch (Exception ex)
    {
        output.Write(String.Format("<div>{0}: {1}</div>", ex.GetType(), ex.Message));
    }
}

...

internal class ListItemCounter
{
    private readonly SPWeb _web;

    public ListItemCounter(SPWeb web)
    {
        _web = web;
    }

    public int CountListItems(string listName, string queryFiler)
    {
        SPList list = _web.Lists[listName];
        SPQuery query = new SPQuery();
        query.Query = queryFiler;
        SPListItemCollection items = list.GetItems(query);

        return items.Count;
    }
}

Esto es importante ya que de este modo nuestro código quedará aislado de algunos de los elementos que el componente Webpart lleva implícitos, como el contexto en el cual la aplicación ASP.Net se está ejecutando; que entre otras cosas da bastante guerra ya que es complicado de reproducir. Y como consecuencia será más sencillo poder realizar las pruebas como veremos.

Monday, September 03, 2007 10:34:15 PM (Hora de verano romance, UTC+02:00)   #    Comments [0]   NET Development | SharePoint-es  | 

Estos días mi querido amigo Gustavo, ha estado pasando un infierno tratando de mejorar el proceso de realizar pruebas unitarias con SharePoint.

Desde mi punto de vista las pruebas con objetos Mock funcionan (luego veremos cómo), pero creo que no siempre son lo más adecuado y esto hay que tenerlo muy en cuenta.

Cuando desarrollamos pruebas unitarias tratamos de ver si existen diferencias entre el comportamiento esperado y el comportamiento observado. Cuando la realización de estas pruebas se convirtió en algo complejo, por la dependencia de nuestro código con sistemas externos surgieron distintas técnicas que nos permiten substituir estos sistemas e independizar nuestras pruebas.

Con estas técnicas, entre las cuales están los objetos Mock, el código que escribimos para realizar la prueba, interactúa con impostores, falsificaciones, espías, objetos simulados etc... Es decir con fantasmas.

Fantasmas, “ectoplasmas” que aparentan ser lo que no son; objetos de verdad. Pero que nos permiten entre otras cosas que nuestra prueba no se detenga por depender de un subsistema externo y muchas veces ajeno a nosotros.

De modo que en la mayoría de los casos, estamos dando por sentado como se comportará ese subsistema, comportamiento esperado. Al predeterminar esto estamos dando por hecho que dicho subsistema funcionará correctamente ó incorrectamente si queremos comprobar que nuestro código sabe responder a las fallas del subsistema, con lo que el subsistema real queda fuera del ámbito de nuestra prueba. Resumiendo, independizamos nuestras pruebas.

Veamos ahora un ejemplo conocido:

public static class Tools
{
    public static string GetCurrentUserName(HttpContext myContext)
    {
        SPWeb myWeb = SPControl.GetContextWeb(myContext);
        SPUser myUser = myWeb.CurrentUser;
        return myUser.LoginName;
    }               
}

Si, es enrevesado ya que estamos usando el contexto de la aplicación web (cosa que deberíamos evitar siempre que podamos, pero eso es otra historia)

Volviendo al código si lo que estamos haciendo es una aplicación ASP.Net que debe interactuar con SharePoint (el subsistema) y queremos realizar una prueba unitaria de este código podemos hacerlo usando objetos Mock.

De este modo nuestra prueba funcionará correctamente sin la necesidad de disponer de una instalación de SharePoint.

Yo personalmente uso TypeMock, que me parece sin lugar a dudas el más completo y con el que menos cuesta (en términos de líneas) realizar los test.

Ejemplo

[Test]
public void GetCurrentUserNameTest()
{
    string expectedUserName = "Sample User";

    MockManager.Init();

    // Hacemos un Mock de SPUser
    // Usando MockManager.MockObject nos permitira crear un objeto sin
    // llamar a su constructor para recuperar la instancia inmediatamente
    MockObject mockSPUser = MockManager.MockObject(typeof (SPUser));

    // Fijamos el valor que será devuelto cuando se solicite el valor de la 
    // propiedad LoginName
    mockSPUser.ExpectGet("LoginName", expectedUserName);
    
    // Devolvemos la instancia para poder usarla
    SPUser spUser = mockSPUser.Object as SPUser;

    // Hacemos un Mock de SPWeb
    MockObject mockSPWeb = MockManager.MockObject(typeof (SPWeb));
    
    // Fijamos el valor que será devuelto cuando se solicite el valor de la
    // propiedad CurrentUser que es el objeto SPUser que creamos anteriormente            
    mockSPWeb.ExpectGet("CurrentUser", spUser);

    // Devolvemos la instancia para poder usarla
    SPWeb spWeb = mockSPWeb.Object as SPWeb;

    // Hacemos un Mock de SPControl
    // En esta ocasion no vamos a instanciarlo ahora, se instanciará automaticamente 
    // cuando se solicite
    Mock mockSPControl = MockManager.Mock(typeof (SPControl));

    // En cualquier caso el método GetContextWeb, devolverá el objeto SPWeb
    mockSPControl.AlwaysReturn("GetContextWeb", spWeb);

    // Tomamos el nombre del usuario
    // Incluso podemos pasarle null (este no es el test de parametros/excepciones)
    string userName = Tools.GetCurrentUserName(null);

    // Comprobamos que el valor esperado es el valor que hemos obtenido
    Assert.AreEqual(expectedUserName, userName, "Correct!!!");

    // Verificamos la prueba
    MockManager.Verify();
}

El código de la prueba nos permite verificar que el método GetCurrentUserName funciona correctamente. Es más no necesitamos depender de SharePoint para probar nuestro código. Ya somos independientes del Subsistema.

Pero si analizamos la prueba, lo único que hemos hecho es prefijar el comportamiento de cada uno de los objetos que usa nuestro método, que como dice un gran amigo, es como decirle al ordenador: Cuándo te pregunte como me llamo, di que me llamo PEPE, y preguntarle ¿Cómo me llamo? a lo cual responderá PEPE, y diremos BIEN, aunque yo me llame JUAN.

Pero eso sí, el maremágnum de pruebas correrá feliz por los mundos de yupi.

Otro tema a tener en cuenta, es, si es necesaria la prueba y cuando debemos realizar este tipo de pruebas ¿Qué estamos probando aquí?, no estamos haciendo un test de excepciones, ni un test de carga, ni un test de delegación etc., ni siquiera una prueba de integración, lo único que estamos haciendo es comprobar que el subsistema de SharePoint, responderá como nosotros esperamos que lo haga. (Cosa que los programadores de SharePoint hacemos habitualmente) De modo que garantizar que SharePoint funciona correctamente no es nuestra tarea. Para nosotros lo importante es el hecho de que interactuamos con un sistema externo y el ámbito de nuestras pruebas debería ser nuestro propio código.

Puede parecer complicado pero yo creo que hay que poner límites a las pruebas, de modo que probaría a ver si estoy controlando todas las excepciones que debería, a la hora de tratar con el subsistema y a asegurarme que los datos que le estoy pasando o que estoy recibiendo de él son los correctos.

Por otro lado cuando desarrollamos en el interior del subsistema (WebParts, Eventos, Workflows, etc.) las cosas pueden cambiar...

Wednesday, August 29, 2007 3:44:01 PM (Hora de verano romance, UTC+02:00)   #    Comments [0]   NET Development | SharePoint-es  | 

Some time ago, I thought write a small tool in order to do CAML queries in plain text, as in SQL "where" clause. I have the idea forgotten, I missed myself in the daily work. Yesterday doing a little modification in a program I remembered this, of course while I was writting a complex CAML query.

I named it "Yet Another CAML Query Tool" aka (YACAML-QT).

By moment it's a little toy, you can write your queries as in a "where" clause, using the same syntax that you'd write in a SQL Sentence. It's is more easy!!!

Updated
Use "=null" as CAML IsNull operator and "<>null" as IsNotNull also you can use dates enclosed into sharp characters #01/01/1990# the sql Like operator is replaced by CAML Contains and the at "@" operator for the CAML BeginsWith. Also support parentheses for operator precedence.

TODO: a pretty interface :-)

 YACAMLQT.zip

(File Updated December 9, 2007)

Tuesday, June 19, 2007 10:50:10 PM (Hora de verano romance, UTC+02:00)   #    Comments [2]   NET Development | SharePoint  | 

Page 1 of 4 in the NET Development category Next Page

Copyright © 2008 Carlos Segura. All rights reserved.