Programando a cuatro manos: Unit Test para SharePoint

Aquí continúa el drama de Unit Test para SharePoint. Esta vez hemos reunido esfuerzos entre nosotros, Carlos Segura (http://www.ideseg.com ) y Gustavo Velez (http://www.gavd.net ), para intentar sacar algo en claro. Programadores del mundo de SharePoint, uníos! …

Unit Test, SharePoint y que es lo importante:

Testeo Unitario es una técnica de programación que permite asegurar que lo que se ha programado hoy cumple las condiciones exigidas, y que las seguirá cumpliendo en el futuro, cuando se hagan modificaciones en el código fuente. Existe numerosa información al respecto, tanto en libros de texto como en internet, así que no repetiremos las bases de la técnica; en resumen, es deseable que se cumplan algunas condiciones:

– El código de Pruebas Unitarias debe ser independiente de cualquier FrameWork que se esté utilizando para garantizar que no hay dependencias, es decir, circunstancias que no se puedan repetir en el futuro. En nuestro caso, no se desea tener que utilizar una instancia de SharePoint para ejecutar el testeo, pues es prácticamente imposible crear dentro de algunos meses/años un sistema de SharePoint que sea exactamente igual al utilizado originalmente

– El código de Pruebas debe ser fácil y rápido de escribir. El (valioso) tiempo de programación se debe utilizar para programar la funcionalidad, no para programar el sistema de pruebas

– El código de prueba tiene que ser igual para todos los desarrolladores en el equipo de desarrollo. Esto tiene que ver con el primer punto: si no existen dependencias (configuraciones, por ejemplo) con el FrameWork utilizado, no hay riesgos de que dos desarrolladores obtengan diferentes resultados. O que el sistema de compilación automática produzca resultados impredecibles (Night Build, Continuous Build, Regretion e Integration Test)

Unit Test es comúnmente utilizado en desarrollo de software, y existen varias herramientas que lo facilitan. Visual Studio (versión profesional y superior) provee todas las herramientas necesarias para generar Unit Test con un clic; si no se dispone de una de estas versiones de Visual Studio, existen herramientas gratuitas que pueden hacer el mismo trabajo (NUnit, http://www.nunit.org/, xUnit, http://www.codeplex.com/xunit y TestDriven, http://www.testdriven.net/ por ejemplo).

Cuando se desean hacer Pruebas Unitarias para SharePoint es necesario utilizar técnicas suplementarias que permitan crear “imágenes fantasmas” de sus clases. Esto se puede conseguir de diferentes formas, por medio de Mocking o Stubbing, por ejemplo

· http://geeks.ms/blogs/gvelez/archive/2007/08/04/mocks-mocking-mockers-y-sharepoint.aspx

· http://geeks.ms/blogs/gvelez/archive/2007/08/12/stubs-stubbing-stubbers-y-sharepoint.aspx

· http://www.ideseg.com/SharePointPruebasUnitarias1.aspx

Otras técnicas, como la utilización de Plantillas o Copias de Seguridad son utilizables, pero no cumplen las condiciones ideales indicadas al principio.

Mocking es una técnica que permite crear objetos “falsos” de un determinado FrameWork, y luego “convencer” al código a probar que tiene que usar los objetos falsos, y no los que obtendrán del FrameWork en su trabajo real. Esta técnica permite cumplir todas las condiciones indicadas. Desafortunadamente, por la forma de programación de SharePoint no es posible usar Mockers tradicionales. La nueva versión de TypeMock (versión 5.0, http://www.typemock.com ) permite crear objetos mock que funcionan bajo estas condiciones, haciéndolo ideal para crear Unit Test para SharePoint. Desafortunadamente, TypeMock es una herramienta comercial, pero se puede bajar del sitio indicado una versión de prueba completamente funcional por 30 días.

Otra de las motivaciones para usar TypeMock, es que el equipo de SharePoint Guidance está usando TypeMock en sus ejemplos; el porqué es simple, a pesar de que existen otras alternativas en el mercado, y algunas de uso libre, ninguna de ellas es capaz de mockear clases selladas o clases que no tienen un constructor público. La API de SharePoint cuenta con innumerables clases y una gran parte de ellas están selladas y carecen de constructores públicos por lo que TypeMock es la única alternativa por el momento.

Mockeando a SharePoint

1 – El caso más sencillo: mockear a SPSIte y SPWeb. El método a probar tiene la siguiente forma:

public static string TestMock_01()

{

    String strReturn = String.Empty;

    try

    {

        using(SPSite mySite = new SPSite(«http://MiServidor»))

        {

            using(SPWeb myWeb = mySite.OpenWeb())

            {

                strReturn = myWeb.MasterUrl;

            }

        }

    }

    catch (System.Exception ex)

    {

        strReturn = ex.ToString();

    }

    return strReturn;

}

Y el método de Pruebas Unitarias con TypeMock seria:

public void TestMethod1()

{

    using (RecordExpectations recorder = RecorderManager.StartRecording())

    {

        SPSite mySiteMocked = new SPSite(«»);

        SPWeb myWebMocked = RecorderManager.CreateMockedObject<SPWeb>();

        recorder.ExpectAndReturn(mySiteMocked.OpenWeb(), myWebMocked);

        recorder.ExpectAndReturn(myWebMocked.MasterUrl, «abcd.master»).RepeatAlways();

    }

    string expected = «abcd.master»;

    string actual;

    actual = Program.TestMock_01();

    Assert.AreEqual(expected, actual);

}

Bajo el estamento “using” se crean objetos SPSite y SPWeb “falsos”. Todos los objetos creados bajo el “recorder” serán después utilizados como objetos de trabajo en la función a testear, en lugar de los objetos “reales” obtenidos del FrameWork de SharePoint. Esto permite garantizar que en el ejemplo, no importa qué tipo de configuración tenga la instalación actual de SharePoint, siempre se le va a “hacer creer” al método a testear que la página maestra del Web se llama “abcd.master”.

Luego de crear los objetos mockeados, se inician las variables a esperar (expected), se ejecuta el método a probar y el resultado se compara con lo esperado.

2 – Yendo algo más lejos: mockeando colecciones. Cuando se desea utilizar la misma sintaxis para crear objetos mockeados de colecciones se encuentran diferentes dificultades. En las colecciones de SharePoint se ha mantenido el diseño existente en versiones anteriores por lo que funcionan como lo hacían en Net 1.1: una clase que implementa IEnumerable y una clase anidada que implementa IEnumerator; para poder recorrer colecciones de objetos mockeados se debe reproducir este comportamiento y lo que a priori puede parecer fácil en realidad no lo es…

En primer lugar se debe mantener una colección de objetos paralela a través del cual podamos devolver un enumerador; se puede ver un ejemplo de esta técnica en (http://dotnetforum.dk/blogs/mac/archive/2007/12/21/first-steps-with-typemock-and-the-sharepoint-api.aspx), lo cual nos obliga en cierta manera a crear métodos para reproducir las colecciones de objetos de SharePoint. En parte motivados por esto y por la necesidad de poder realizar los tests de la manera más sencilla posible hemos creado un conjunto de clases envoltorio que nos permitirán extender los objetos a mockear de SharePoint de una manera más elegante.

Por ejemplo, para el siguiente método a probar, en el que se necesita recorrer la colección de Listas en el Web de más alto nivel:

public static string TestMock_02()

{

    string strReturn = String.Empty;

    try

    {

        using (SPSite mySite = new SPSite(«http://MiServidor»))

        {

            using (SPWeb myWeb = mySite.OpenWeb())

            {

                int intTeller = 0;

                foreach (SPList oneList in myWeb.Lists)

                {

                    Debug.WriteLine(oneList.Title);

                    intTeller++;

                }

                strReturn = intTeller.ToString();

            }

        }

    }

    catch (Exception ex)

    {

        strReturn = ex.ToString();

    }

    return strReturn;

}

Se puede utilizar un método de prueba con la siguiente sintaxis:

[TestMethod]

public void TestMethod2()

{

    MockSPSite mockSite = new MockSPSite(«TestSite»); // 1

    MockSPWeb mockWeb = new MockSPWeb(«TestWeb»); // 2

    MockSPList mockList0 = new MockSPList(«MyList0»); // 3

    MockSPList mockList1 = new MockSPList(«MyList1»);

    MockSPList mockList2 = new MockSPList(«MyList2»);

    mockWeb.Lists = new MockSPListCollection(new[] // 4

    {

        mockList0,

        mockList1,

        mockList2

    });

    mockSite.ExpectGetAlways(«RootWeb», mockWeb.GetInstance()); //5

    SPWeb WebMocked = mockWeb.GetInstance(); // 6

    using (RecordExpectations recorder = RecorderManager.StartRecording()) // 7

    {

        SPSite SiteMocked = new SPSite(«»); // 8

        recorder.ExpectAndReturn(SiteMocked.OpenWeb(), WebMocked); // 9

    }

    string expected = «3»; // 10

    string actual;

    actual = Program.TestMock_02();

    Assert.AreEqual(expected, actual);

}

Aquí se pueden distinguir tres zonas con código:

– En la primera parte, líneas 1 a 7 se crean todos los objetos “falsos” necesarios. Líneas 1 y 2 crean objetos del tipo SPSite y SPWeb. Las líneas bajo el numero 3 crean tres Listas mockeadas y en la línea 4 se agregan las Listas a la Colección de Listas del objeto SPWeb mockeado. En la línea 5 se agrega a su vez el objeto SPWeb como la Web de nivel superior del objeto SPSite mockeado y finalmente, en la línea 6 se crea un objeto del tipo SPWeb nuevo basado en el objeto SPWeb mockeado. Esto es necesario para el código en la segunda zona, que no permite utilizar objetos mockeados directamente

– En la segunda zona, líneas 7 a 9, se crean los objetos que se le van a entregar a la clase a testear por medio de un “recorder” de TypeMock. En la línea 8 se crea un objeto SPSite que va a substituir el SPSite en la clase a testear, y en la línea 9 se le asigna el valor del objeto SPWeb mockeado con las Listas creadas en la primera parte.

– La tercera zona, desde la línea 10, incluyen el código para los valores esperados y los valores que regresan del método a testear y los comparan en la aserción.

La primera parte del código utiliza las clases envoltorio comentadas anteriormente (MockSPSite, MockSPWeb, etc). Estas clases tienen la forma:

namespace SPTypeMock.Core

{

    public class MockSPSite : MockSP<SPSite>

    {

        public MockSPSite(string url) : base(true)

        {

            if (string.IsNullOrEmpty(url))

        {

        throw new ArgumentNullException(«url»);

        }

            Mock.ExpectGetAlways(«Url», url);

        }

    }

}

Estas clases sirven para simplificar la creación de los objetos mockeados, y no son más que herencias de tipos definidos (SPSite en este caso), con algunos constructores específicos y métodos para conectar sus colecciones de objetos. Las clases de envoltura permiten reutilizar el código una y otra vez, sin que la escritura de las pruebas signifique el tener que escribir mas código que el código a testear (una de las condiciones señaladas al principio).

Todas estas clases se encuentran en la biblioteca SPTypeMock que podéis encontrar en CodePlex en http://www.codeplex.com/SPTypeMock .

SPTypeMock usa dos clases, la primera es MockSP<T> que es una clase abstracta desde la cual mockearemos los objetos de SharePoint, la única peculiaridad de esta clase está en el constructor, que tiene un parámetro bolean, que se encarga de añadir la propiedad ID de la clase; las clases de SharePoint tienen un ID, en la mayoría de ellas es un Guid, pero en otras como SPListItem es un int.

La propiedad Mock de esta clase nos da acceso al objeto mock que se encuentra en el interior, debemos usar esta propiedad para establecer las expectativas.

El método GetInstance, se encarga de devolvernos una instancia real del objeto mockeado.

La segunda clase es MockSPCollection<TCollection,TItem> que es otra clase abstracta desde la cual heredaremos las colecciones de SharePoint. MockSPCollection, hereda a su vez de List<TItem> de modo que así conseguimos tener la colección paralela necesaria para trabajar con las colecciones de SharePoint.

Disponemos de dos constructores uno que se encargará de crear colecciones vacías y otro al que podemos pasarle una lista de elementos que formarán parte de la colección. Al obtener una instancia de la colección por medio de GetInstance(), establecemos que la interfaz IEnumerable será obtenida de la lista; también aquí nos encargamos de mockear el index por defecto que será a través de un int.

Si las colecciones que deseamos usar tienen otros índices como SPListCollection, que tiene un índice por título de la lista y otro por Guid, podemos sobrescribir el método CustomIndex en las clases heredadas implementando dicha funcionalidad.

No todo es perfecto…

No todo ha sido solucionado, algunos problemas quedan aún por resolver:

– La instalación de TypeMock no permite seleccionar un directorio de instalación, lo que limita la utilización de programas de compilación automático, como Cruise Control por ejemplo

– Los objetos mockeados creados en las dos primeras zonas de la clase de prueba son creados en un Thread diferente al utilizado por SharePoint al crear los objetos en el método a testear. Esto tiene como consecuencia que si se desea destruir los objetos al final del método a probar (por medio de un “using” para el SPSite y/o el SPWeb, o por medio de un “Dispose()” al final del método), SharePoint producirá un error en sus logs indicando “ERROR: request not found in the TrackedRequests. We might be creating and closing webs on different threads”

– Es necesario crear aun muchas más clases de envoltorio

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

Mocking a SharePoint, la historia continua

Hace algo más de un año yo comencé una serie de pequeños artículos en este mismo sitio sobre Pruebas Unitarias para SharePoint (“Mocks, Mocking, Mockers y SharePoint” , “Stubs, Stubbing, Stubbers y SharePoint” y “Morir con las botas puestas o Unit Test con SharePoint”). Después de mucho luchar con el asunto, mi conclusión (temporal) fue que el asunto era entre dificilísimo e imposible.

El tema ha regresado recurrentemente entre tanto. Inclusive hablándolo con Carlos Segura alguna vez, el salió con una forma de hacer Pruebas Unitarias (http://www.ideseg.com/BeginnersGuideToTDWebPartDevelopment.aspx) que en realidad nunca me ha gustado por complicada y “poco elegante” y por no eliminar la dependencia con SharePoint.

De lo que se trata en resumen es de como realizar Pruebas Unitarias para código de SharePoint. El problema es bastante sencillo: es necesario eliminar de una u otra forma las dependencias del código con respecto al API de SharePoint para poder repetir las pruebas una y otra vez sin depender del estado actual del Portal; inclusive, si es posible, sin depender DE NINGUNA MANERA de SharePoint. El método de Carlos se basa en la creación de plantillas para Sitios o Listas que se cargan dinámicamente al principio de la Prueba Unitaria y se destruyen al final. Esto garantiza que se mantenga un estado constante en SharePoint, pero, fuera de ser engorroso y (extremadamente) lento, solo permite probar algunas cosas del API de SharePoint (todo lo que tenga relación con Sitios y Listas), pero no permite hacer pruebas de la parte administrativa, por ejemplo. Y siempre es necesario tener una instalación de SharePoint detrás, con todas las subordinaciones necesarias de URL, configuración de usuarios, etc.

Para eliminar realmente las dependencias es necesario usar Mockers o Stubbers (los artículos mencionados al principio dan una pequeña idea de que son estas dos cosas). Hace un par de semanas, TypeMock, uno de los frameworks para Mockers, ha salido al mercado con una nueva versión que permite mucho más de lo que era posible de hacer anteriormente. Desafortunadamente la herramienta (http://www.typemock.com) no es gratis, pero es posible descargar una versión de prueba de 30 días. El problema para mockear a SharePoint es que muchas (demasiadas) de sus clases son selladas y/o no tienen constructores públicos, por lo que para poder utilizar Mockers “tradicionales” es necesario crear primero Interfaces, y luego mockear la clase indirectamente. Como se podrán imaginar, esto es trabajo imposible de hacer, teniendo en cuenta que el Modelo de Objetos de SharePoint es tan increíblemente extendido.

Pues bien, la nueva versión de TypeMock permite trabajar también con este tipo de clases, lo que facilita el asunto considerablemente. TypeMock viene con diferente tipos de sintaxis, una de las cuales, “Natural Mock” es bastante sencilla de utilizar. El siguiente es un ejemplo idiota, pero que sirven para ver cómo funciona el asunto. Queremos crear una Prueba Unitaria del siguiente método, que simplemente devuelve el URL de la página maestra de un sitio:

 

public string MethodOne(string SiteUrl)

{

    String strReturn = String.Empty;

    SPSite mySite = null;

    SPWeb myWeb = null;

    try

    {   

        mySite = new SPSite(SiteUrl);

        myWeb = mySite.OpenWeb();

        strReturn = myWeb.MasterUrl;

    }

    catch (System.Exception ex)

    {

        strReturn = ex.ToString();

    }

    finally

    {

        if (myWeb != null) myWeb.Dispose();

        if (mySite != null) mySite.Dispose();

    }

    return strReturn;

}

 

Si queremos hacer Pruebas Unitarias sobre este método, necesitamos eliminar la dependencia del SPSite y del SPWeb (no queremos tener que tener una instalación de SharePoint detrás, que probablemente va a cambiar constantemente, por lo que nunca sabremos con certeza que es lo que nos va a devolver). Nuestro método de prueba usando TypeMock y la infraestructura por defecto de Visual Studio sería más o menos:

 

[TestMethod()]

public void MethodOneTest()

{

    using (RecordExpectations recorder = RecorderManager.StartRecording())

    {

        SPSite mySiteMocked = new SPSite(«»);

        SPWeb myWebMocked = RecorderManager.CreateMockedObject<SPWeb>();

        recorder.ExpectAndReturn(mySiteMocked.OpenWeb(), myWebMocked);

        recorder.ExpectAndReturn(myWebMocked.MasterUrl, «abcd.master»).RepeatAlways();

    }

    MisObjetos target = new MisObjetos();

    string SiteUrl = string.Empty;

    string expected = «abcd.master»;

    string actual;

    actual = target.MethodOne(myListMocked);

    Assert.AreEqual(expected, actual);

    //Assert.Inconclusive(«Verify the correctness of this test method.»);

}

 

La parte al principio (alrededor del “using”) es en donde creamos nuestro “Mock”, es decir, allí “falsificamos” lo que SharePoint deberá hacer por su cuenta cuando el código funcione realmente. En el primer renglón se crea una instancia de SPSite; note que no es necesario entregarle un URL para crearla, pues el objeto será de todas formas falso, así que no vale la pena crear algo “real”. El segundo renglón crea un objeto SPWeb utilizando una sintaxis especial para la creación de objetos que serán modificados posteriormente (el objeto SPSite es utilizado solamente como referencia, pero no será modificado). La tercera línea le indica al mockeador que el objeto SPWeb creado corresponde al objeto que puede encontrar en el objeto SPSite.OpenWeb. Finalmente la cuarta línea simplemente le asigna un valor a una propiedad del objeto SPWeb (el nombre de su Pagina Maestra).

Las líneas siguientes son generadas automáticamente por Visual Studio, y lo único que es necesario de modificar es el valor esperado. Lo que ocurre al ejecutar la Prueba Unitaria es muy sencillo: el framework de TypeMock le “hace creer” al código que tiene un objeto SPSite y otro SPWeb y ejecuta el método con esos objetos. Esto es sencillo de explicar de esta manera, pero “por debajo de la mesa” ocurren muchas otras cosas que no tienen importancia para el caso dado.

Pues bien, estas son las conclusiones:

Las buenas noticias: Este nuevo framework hace posible crear Pruebas Unitarias para SharePoint. Punto.

Las malas noticias 01: Se ha dado cuenta que para tres renglones de código “de trabajo” se necesitan cuatro líneas de código “de prueba”? Y un ejemplo más sencillo simplemente no me he podido inventar. La realidad es que es necesario mockear cada uno de los objetos utilizados, lo que rápidamente conlleva a que el método de prueba sea más largo (y hasta complicado) que el método a probar.

Las malas noticias 02: Probablemente por mí propia ineptitud, estoy teniendo enormes problemas al intentar mockear colecciones de objetos. Me he dado cuenta que los problemas ocurren porque necesito implementar una Interface IEnumerator que parece que SharePoint utiliza de uno u otra rara manera… en cualquier caso, si intento mockear un SPListCollection, por ejemplo, siempre termino en el basurero de los errores terminales…

Coletilla: Probablemente seguiré escribiendo sobre el asunto por un par de artículos más. Si a alguien le da la rara idea de experimentar con este (apasionante) asunto, por favor, envíenme un E-mail, y de pronto entre todos encontramos soluciones a los problemas que se ven venir…

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

Primer Simposio Latinoamericano de SharePoint

El 11 de noviembre de 2008 se realizará el Primer Simposio Latinoamericano de SharePoint en San Jose de Costa Rica.

Las inscripciones están abiertas, y el cupo es limitado, así que si desea participar se debe dar prisa. Información sobre formas de inscripción y más detalles puede encontrar en el sitio de Ricardo Muñoz (http://mundomoss.blogspot.com/2008/09/primer-simposio-lationamericano-de.html)

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

Cromeando a SharePoint

Y como se ve SharePoint en Chrome, la nueva invención de Google?

 

Igual a como se ve en Internet Explorer 8 (Beta 2):

 

Investigación no científica realizada en 10 minutos:

Se ve alguna diferencia?

– No

Es más rápido?

– No (tal vez una fracción de un milisegundo más rápido, pero es imposible de medir [lo he intentado sin éxito])

Usa menos memoria?

– No. Para mostrar una pantalla, Chrome usa tres procesos de 10.192 K, 30.400 K y 20.240 K (más de 60.000 K en total) y IE con la misma pantalla usa uno solo de 50.512 K

Alguna otra diferencia?

– La conocida diferencia de la consola de configuración de WebParts: Chrome la muestra de la misma forma que FireFox, pero tiene la misma funcionalidad que IE.

Conclusión:

O SharePoint lo hace muy bien, o Google no lo hace tan mal…

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