blog counter
August 2007 - Artículos - SkunkWorks

August 2007 - Artículos

Morir con las botas puestas o Unit Test con SharePoint

Después de darle vueltas al asunto por todos lados, chocarme con todos los muros del mundo (e intentar romperlos a cabezazos), discutir el tema con un montón de gente (Carlos Segura Sanz (el mejor conocedor de SharePoint de todo el mundo), Rolf Eleveld (el mejor desarrollador de todo el mundo), Jan Cirpka (el mejor arquitecto de todo el mundo), entre otros), el asunto se puede resumir rápidamente, y las conclusiones son aun más rápidas:

De que se trata (premisas):

- Crear Unit Tests para business layers de SharePoint. Porque, para que y que son Unit Test, lo pueden ver en el posting de Rodrigo Corral "¿Deben los test unitarios usar la base de datos?".

- Unit Tests tienen que ser rápidos de crear (el tiempo de programación es principalmente para programar, no para crear Unit Test)

Porqué con SharePoint (postulado):

- Porque Unit Test son importantes si quieres hacer una arquitectura un poquito "limpia" de tres capas, porque Unit Test te "garantizan" que algo que desarrollas hoy siga funcionando dentro de seis meses cuando cambies la funcionalidad de las clases, porque Unit Test te aseguran que todos los desarrolladores están mirando hacia el mismo lado (aunque no necesariamente caminando por el mismo camino)

- Porque hasta ahora a nadie se le ha metido la rara idea de hacer Unit Test para SharePoint; o mejor dicho, a mucha gente le ha dado la rara idea, pero nadie ha salido con algo productivo

Lo probado (anteriormente):

- Mocking: vea el posting "Mocks, Mocking, Mockers y SharePoint". Conclusión: no funciona

- Stubbing: vea el posting "Sutbs, Stubbing, Stubbers y SharePoint". Conclusión: no funciona a la primera. Ni a la segunda, ni a la tercera. Es posible que haciendo Stubs para TODOS los NameSpaces de SharePoint podría funcionar, pero hacer algo así es trabajo gigantesco, y sin ninguna seguridad de que funcione. El último problema que me encontré fue que los métodos a probar con el Unit Test no aceptaban los Stubs porque no tenían la firma correcta (y, por supuesto, Microsoft no me va a prestar su Secret Key para compilar mis Stubs).

Lo probado (últimamente):

- BackUp / Restore: Salió de una conversación con Jan. Qué pasa si se abandona la idea de Mocking y Stubbing, y se vuelve a la forma "normal" de hacer Unit Test usando una instancia de SharePoint? Aquí hay varios puntos a tener en cuenta:

1 - Después de que cada Unit Test se ha ejecutado, hay que garantizar que el estado de SharePoint sea retornado a como era antes de ejecutar el test, de otra forma el Unit Test puede que devuelve resultados no confiables (usando Mocks o Stubs no existe este problema)
2 - Cada desarrollador en el grupo tiene que disponer del mismo SharePoint (del contenido, por supuesto), de otra forma, cada desarrollador obtendrá diferentes resultados
3 - Si ejecuto mis Unit Test dentro de seis meses, el resultado tiene que ser el mismo que el que he recibido hoy

Todos estos puntos se pueden cumplir si se hace un BackUp de SharePoint, se guarda en un sitio central (SourceSafe? TFS?), y se restaura después de cada ejecución del Unit Test. Los Unit Test tienen un evento de inicialización (TearUp) y otro de finalización (TearDown), de tal forma que en el evento de finalización se ejecute un Restore del BackUp. Y SharePoint dispone de su herramienta de administración stsadm, que permite crear Backups y Restores rápida e indoloramente. El TearDown de un Unit Test de Visual Studio 2005 aparecería más o menos como:

     [TestCleanup()]
     public void MyTestCleanup()
     {
         Process RestoreSharePoint = new Process();
         RestoreSharePoint.StartInfo.FileName = @"C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\STSADM.EXE";
         RestoreSharePoint.StartInfo.Arguments = @"-o restore -url http://wssen -filename c:\temporary\backup.dat -overwrite";
         RestoreSharePoint.Start();
     }

- Problemas: por supuesto, siempre hay problemas... Un Restore de SharePoint no ejecuta directamente, sino por medio de un Job. Esto significa, que el Restore puede iniciar entre ahora mismo, y solo los dioses saben cuándo. Segundo problema (me parece que es un bug, pero no puedo asegurarlo), es que si se intenta hacer un Restore cuando otro Restore está ejecutando, toda la instalación de SharePoint se dirige hacia el cielo de las instalaciones destruidas de SharePoint... en serio, la cantidad de instalaciones de SharePoint que me he cargado en los últimos días son incontables, y lo peor de todo es que no hay (o por lo menos yo no lo he podido encontrar) manera de hacer que SharePoint funcione de nuevo; utilizando el comando "execadmsvcjobs" se puede forzar la ejecución de los Jobs, pero con el comando empiezan a ejecutar TODOS los Jobs, uno por uno y uno detrás del otro, así que mucha ganancia en tiempo no se obtendrá. Otro problema es la restauración en instancias de SharePoint que no son iguales a la original (si dentro de los consabidos 6 meses intentas hacer un Restore en un SharePoint que no se llama "aaa" sino "bbb", y no funciona, tienes lo mismo que no tener nada, es decir, no tienes nada)

- Conclusión: funciona, por supuesto que funciona, y hasta funciona de lo más bien. Un BackUp de una instancia de SharePoint para desarrollo no es demasiado grande (1 MB máximo, por lo que se puede guardar una copia para el futuro sin problemas), y si se guarda en un share en la red, todos los desarrolladores la pueden utilizar. Pero no me sirve si un desarrollador arranca un Test dos veces seguidas y restaura la copia al mismo tiempo: su SharePoint dejaría de funcionar. Tampoco si dentro de 6 meses no lo puedo usar. Me puede servir en el servidor de generación ("Continuous Integration" en un "Build Server") o en un servidor de generación nocturna, en donde solamente de vez en cuando se ejecutan los Unit Test. Algo es algo...

Lo para probar (en el futuro cercano)

- Provisioning: Bueno, si un Restore tiene tantos problemas, que tal si el sitio se provisiona de nuevo en el TearDown? Es decir, estas probando una clase que modifica una Lista (le agrega un elemento, por ejemplo). En el TearDown, después de ejecutar el Unit Test, se ejecuta un pequeño programa que elimina el nuevo elemento, y la Lista vuelve a su estado original. Perfecto,

- Problemas: pero... este es un caso muy sencillo. En casos más complicados (y reales), suceden cosas más complicadas, y sobre todo, cosas en cadena, por ejemplo: crear un Web, agregarle algunas Listas, configurar derechos, agregar usuarios, etc... el programita ya no es mas "ita" sino "ote", y volvemos a la premisa original de que no vale la pena pasar 75% del tiempo de programación programando las clases de Unit Test.

- Soluciones (?): Crear un programa para "Provisioning"? algo así como un programa en donde puedas decir, de una forma más o menos wysiwyg: deshaga todo lo que hay hecho en SharePoint, créeme un programa (aplicación de consola) que me haga una Web, con estas Listas y estos derechos para estos usuarios, y luego subalo todo a SharePoint (el TearDown borra todo el contenido en SharePoint y lo vuelve a crear en el estado original). hmmmmmm...

El drama continúa...

Gustavo - http://www.gavd.net/servers/
Escriba un Comentario que me haga reir...

Publicado por Gustavo Velez con 4 comment(s)
Archivado en:

Stubs, Stubbing, Stubbers y SharePoint

 

Continuando con la lucha para crear Pruebas Unitarias para SharePoint (vea el posting sobre Mocks, Mocking, Mockers y SharePoint)...

Resumidas cuentas, de lo que se trata es de crear objetos "falsos" de SharePoint que se puedan utilizar en Unit Test de una forma fácil, rápida e indolora. Como estoy muy lejos de ser un conocedor de teoría de pruebas de software, he estado buscando información al respecto, y me he dado cuenta que lo que ando buscando en realidad son Stubs, no Mocks. Aunque Mocks pueden funcionar como Stubs (pero no al contrario), el asunto es un poco más claro ahora (?). Hay un articulo bastante bueno sobre la diferencia entre los dos: "Mocks aren't Stubs" de Martin Fowler. Para que no se tengan que leer un artículo largo y pesado (aunque interesante), les cuento que un Mock falsifica y verifica la conducta del objeto, mientras que un Stub simplemente falsifica al objeto, sin importarle que está pasando con sus métodos y propiedades.

Después de luchar horas y horas con el asunto, sigo sin ver la luz. Mejor dicho, ya he visto la luz del amanecer dos días seguidos (después de todo un desarrollador no necesita dormir más de dos horas de cada 24, como todos sabemos), sin encontrar el camino, y ya estoy empezando a pensar que es porque simplemente no existe.

Veamos lo que he intentado hasta ahora:

1 - TypeMock es un producto comercial (mi jefe va a tener que pagar los 150 dólares por nada) que, según las referencias, es de lo mejor que se puede encontrar. Para crear objetos mockeados, TypeMock intercepta las llamadas al CLR (Common Language Runtime) del .NET FrameWork, y retorna valores mockeados sobre-escribiendo los valores originales completamente. Interesante... TypeMock permite crear un objeto falso (un Stub) de una forma fácil y rápida:

     MockObject myLinkA = MockManager.MockObject(typeof(SPLink));
     SPLink myLink = (SPLink)myLinkA.Object;

La primera línea crea un objeto del tipo MockObject, que por dentro tiene un objeto del tipo SPLink (de SharePoint). La segunda línea se lo entrega a un objeto de SharePoint (myLink) por medio de un cast. Esta segunda línea es necesaria para extraer el objeto de SharePoint del objeto de TypeMock, de otra forma la llamada al método que se está probando generara una excepción (mejor dicho, hay que comerse la banana sin la cascara).

Con algunos objetos de SharePoint funciona bastante bien, como es el caso con SPLink. Con muchos otros no, como por ejemplo con SPList. Después de mucho buscar, al descompilar el Microsoft.SharePoint.dll me he enterado porqué: SPLink tiene un constructor que crea valores por defecto cuando se instancia al objeto; SPList tiene un constructor a lo bárbaro, es decir, crea el objeto sin valores por defecto. Por esto, al intentar ejecutar la clase de prueba se obtiene un error de lo más bonito ("Test method TestProject.gavdTest.myTitleTest threw exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object..").

Otro problema es que no es posible cambiar los valores de propiedades en el Stub. Si después de crear el MockObject se intenta algo como:

     MockObject myListA = MockManager.MockObject(typeof(SPList));
     myListA.ExpectGet("Title", "Hola Gustavo");

el objeto no produce ningún error ni nada parecido, pero simplemente no cambia el valor (Title es read/write).

Conclusión: mientras las clases de SharePoint no tengan un constructor que defina valores por defecto, no se producirán más que errores con TypeMock. También la imposibilidad de cambiar los parámetros (ni siquiera los read/write) lo hace inusable.

2 - RhinoMock es gratis y bastante poderoso, y permite crear Stubs directamente:

     SPList myList = (SPList)MockRepository.GenerateStub(typeof(SPList));

que produce un error sobre un proxy que debe ser generado en alguna parte ("Test method TestProject.gavdTest.myTitleTest threw exception: System.MissingMethodException: Can't find a constructor with matching arguments ---> System.MissingMethodException: Constructor on type 'SPListProxyc05cefac21f14e41ae4fefa8c5e791da' not found..")

Y de todas formas es menos poderoso que TypeMock. Por ejemplo, no es posible crear mocks de clases selladas, o de interfaces privadas o de métodos no-virtuales. Una rápida mirada a las clases de SharePoint muestra que más o menos dos tercera partes de las clases son selladas, así que por este lado no vamos a llegar muy lejos.

Conclusion: mientras las clases de SharePoint sean selladas, nada de RhinoMock... por no decir nada sobre el misterioso proxy.

3 - Creando Stubs directamente. Después de mucho buscar, me he encontrado con NStub, un pequeño programa que crea Stubs directamente desde el ensamblado. La idea es bastante sencilla: crear una clase con la misma firma del ensamblado de SharePoint, algo por el estilo a:

namespace Microsoft.SharePoint
{
     public class SPLink : Object
     {

         public SPLink()
         {
         }

         private string _ServerRelativeUrl;
         public string ServerRelativeUrl
         {
         get { return _ServerRelativeUrl; }
         }
    ... etc...

Y luego utilizar esta clase para instanciar el objeto a utilizar, y no la clase real de SharePoint (quitando la referencia a Microsoft.SharePoint). Mejor dicho, si vamos a falsificar el asunto, lo falsificamos de punta a punta. Lo malo es que tampoco funciona:

"Test method TestProject.gavdTest.myLinkUrlTest threw exception: System.ArgumentException:
The member specified (myLinkUrl)could not be found. You might need to re-generate your private accessor,
or the member may be private and defined on a base class. If the latter is true, you need to pass the type
that defines the member into PrivateObject's constructor."

El método es publico, así que ese no es el problema. La cosa va más por el problema de que el constructor que el objeto espera es diferente al constructor (vacio) que se le ha entregado. La firma del constructor original es:

    internal SPLink (SPWeb web, char cStatus, string sUrl, string sUrlParameter, Guid gWebId, Guid gFieldId)

así que si quiero agregar un constructor a mi Stub con esta firma, tengo que tener un objeto del tipo SPWeb. Como no puedo usar los objetos originales de SharePoint (los estamos falsificando, se acuerdan?), tengo que tener otro Stub para el SPWeb... y aquí se me fue la luz... o mejor dicho, la luz de la mañana apareció otra vez, y se me quitaron las ganas de continuar.

Conclusión: El camino del aventurero... funciona o no funciona? Ni idea, pero crear Stubs para las mil y pico de clases de SharePoint es trabajo de gigantes. Escribir un programita que mire con reflexión al ensamblado y me genere los Stubs automáticamente? Brillante idea (o mejor dicho, que haga algo parecido a NStub, pero más completo y con menos basura).

4 - Algo mas para intentar?:

- Crear Interfaces de las clases de SharePoint e intentar crear Mocks sobre las Interfaces? (algo así como el camino inverso: normalmente creas la Interface primero y luego las clases con la lógica, no al revés)

- Abandonar la idea? (hasta ahora he encontrado a dos personas en Internet que DICEN que han hecho Unit Tests para SharePoint, pero ninguna de las dos va mas allá de decir que lo han hecho, sin mostrar ni una letra de código)

- Irme a dormir? Buena idea...

Gustavo - http://www.gavd.net/servers/
Escriba un Comentario que me haga reir...

Publicado por Gustavo Velez con 15 comment(s)
Archivado en:

Mocks, Mocking, Mockers y SharePoint

 

Alguien ha intentado usar Mocks para pruebas unitarias ("Unit Test") con SharePoint?

A ver si nos entendemos. Supongo que Unit Test no es desconocido... dicho en palabras sencillas, es la forma que nosotros, pobres desarrolladores de software, tenemos para asegurarnos a nosotros mismos (pobres ilusos) y al mundo exterior (a nuestros clientes, para que nos paguen más dinero) que no estamos metiendo las patas de una forma vergonzosa. Unit Test tienen varias ventajas: acallar nuestros remordimientos pues no entregamos software sin ser probado apropiadamente, asegurarnos que nada se ha roto cuando cambiamos la lógica interna del software, garantizando que todo seguirá funcionando más o menos bien, tal como ocurría antes de hacer el cambio, poder ejecutar las pruebas automáticamente después de cada compilación (TFS de Microsoft, NAnt), etc., etc., etc.

Hay un montón de herramientas para hacer Unit Test regadas por Internet (MbUnit, NUnit, csUnit, TestDriven.net) e, inclusive, las tenemos incluidas en nuestra herramienta de programación favorita (en la versión Team Foundation Server Developer de Visual Studio 2005/2008, por la que hay que pagar bastante dinero para hacer lo que se puede encontrar gratis en Internet). Y un montón de teoría y teoréticos que nos intentan convencer que hacer Unit Test es lo mas importante en desarrollo de software después del estamento "if". Todo muy interesante y muy importante, pero con un solo problema: todo el software de ayuda y todas las teorías al respecto están hechas para trabajar con clases que funcionan por sí mismas:

Nota: las imágenes me las he robado del sitio de TypeMock.NET sin permiso de los autores.

La imagen muestra una clase nuestra ("Production Class") que es "testeada" por medio de otra clase ("Unit Test"). Desafortunadamente, la realidad es diferente, y sobre todo la realidad cuando estás trabajando con SharePoint (o con cualquier otro servidor "externo"):

En este caso, los datos que el Unit Test recibe son basados en sistemas "externos" (Data Bases, SharePoint, etc), que requieren configuración, y que, a su vez, dependen de otros sistemas externos (SharePoint depende de SQL, por ejemplo). Esto produce tres problemas para el Unit Test:

1 - Repetitividad: el sistema externo tiene que mantenerse con los mismos datos, de tal forma que los datos que el Unit Test espera ("Expected values") para comparar con los datos calculados ("Actual values") sean consistentes a lo largo del tiempo: si quieres ejecutar tus Pruebas Unitarias dentro de un año, tienes que garantizar que SharePoint tenga dentro de un año los mismos datos.

2 - Uso en grupo: Es normal que cuando estas desarrollando con SharePoint, cada desarrollador tenga su propia copia local de SharePoint (y, con frecuencia, de SQL), por eso de que los ensamblados de SharePoint no se pueden usar fuera del servidor, por eso de que si haciendo cosas raras te cargas a SharePoint no quieres detener el trabajo de todo el mundo, y por eso de que Microsoft nunca ha podido darnos una depuración remota que funcione. Para garantizar que los Unit Test (que son iguales para todos los desarrolladores) produzcan los mismos resultados, hay que garantizar que todas las copias locales de SharePoint sean idénticas

3 - hmmmm... el tercer punto... tenía un tercer punto, pero se me olvido... será por eso de que las células grises se agotan a pasos agigantados...

En cualquier caso, la respuesta para el problema es un "Mock": hacerle creer a tus clases que están recibiendo la información de una fuente "exterior", cuando en realidad están recibiéndola desde otra clase "falsa" que has creado tu mismo:

Ideal: puedes garantizar que los datos a consultar siempre sean iguales en el tiempo y para todos los desarrolladores!

Hay varias herramientas (gratis y comerciales) para crear Mocks (Rhino Mocks, TypeMock.Net, EasyMock.NET, NMock, NMock2, entre otras) unas mejores que otras, unas más fáciles de usar que otras, todas con el mismo problema: es prácticamente imposible utilizarlas para "simular" a SharePoint. Veamos:

Una clase con un método (bastante idiota) que nos puede servir de ejemplo:

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

El método recibe el HttpContext de una WebPart, por ejemplo, y retorna el nombre del usuario actual. Ahora el método de Prueba Unitaria, tal como es generado por Visual Studio 2005:

     [DeploymentItem("TestConsoleApplication1.exe")]
     [TestMethod()]
     public void GetCurrentUserNameTest()
     {
         object target = TestProject1.TestConsoleApplication1_Class1Accessor.CreatePrivate();

         TestProject1.TestConsoleApplication1_Class1Accessor accessor = new TestProject1.TestConsoleApplication1_Class1Accessor(target);

         HttpContext myContext = null; // TODO: Inicializar en un valor adecuado

         string expected = null;
         string actual;

         actual = accessor.GetCurrentUserName(myContext);

         Assert.AreEqual(expected, actual, "TestConsoleApplication1.Class1.GetCurrentUserName no devolvió el valor esperado.");
         Assert.Inconclusive("Verifique la exactitud de este método de prueba.");
     }

El problema es visible inmediatamente: el objeto "myContext" tiene que ser inicializado para poder utilizar el método; este es el trabajo que tiene que hacer un Mock: crear un objeto "falso" que tenga el mismo contenido del objeto "verdadero", pero sin tener que utilizar a SharePoint. Para aquellos masoquistas interesados en saber cómo se puede programar este tipo de Mocks, hay un ejemplo en internet que utiliza algo así como 300 líneas de código para mockear un HttpContext ("Mocking HttpContext using TypeMock.Net").

Todo el rollo que estoy escribiendo se refiere precisamente a este problema: si para escribir un Unit Test (8 líneas de código), tengo que agregar otras 300 solamente para falsificar un HttpContext, me voy a pasar 95% del (escaso) tiempo que tengo disponible para crear código en crear clases falsas... sin decir nada de las otras mil y pico clases de SharePoint que también habría que mockear...

Como decía al principio, para un pobre pica-código como yo, que no tiene mucha idea sobre el trabajo que hacen los señores (y damas) mayores que se ocupan de hacer pruebas de software, y que solamente quiere hacer un Unit Test sencillito para SharePoint, esto es demasiado trabajo. No sería posible que alguien nos produjera una librería de Mocks para SharePoint y un FrameWork para utilizarla, compilada y lista para ser usada (aunque la tengamos que comprar), de tal forma que podamos hacer nuestro trabajo (programar y probar SharePoint) de una forma fácil y rápida?

Se aceptan sugerencias...

Gustavo - http://www.gavd.net/servers/
Escriba un Comentario que me haga reir...

Publicado por Gustavo Velez con 20 comment(s)
Archivado en: