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 FilesCommon FilesMicrosoft Sharedweb server extensions12BINSTSADM.EXE”;
         RestoreSharePoint.StartInfo.Arguments = @”-o restore -url http://wssen -filename c:temporarybackup.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…

3 comentarios en “Morir con las botas puestas o Unit Test con SharePoint”

  1. Veo dolor, mucho dolor 🙂

    No tengo ni idea de SharePoint, pero si lo que estás intentado es crear test unitarios para una aplicación ya diseñada y que está llena de dependencias con las librerías de SharePoint… puede ser una pesadilla.

    En Asp.Net llevamos años esperando que alguien cree una librería que sustituya a HttpContext,HttpRequest, etc… y SharePoint a vista de pájaro parece una librería más compleja e intrusiva.

    En caso de que quieras tener test unitarios para una futura aplicación, supongo que podrás injectar una abstracción que contenga las dependencias sobre el framework de SharePoint, y que todo el código de tu capa de negocio dependa sobre esa abstracción.
    En los tests creas un mock: ISharePoint sh = Mocks.CreateMock() y compruebas que las llamadas son correctas.

    Pero insisto, intuyo que intentas crear una librería que te permita testear cualquier aplicación SharePoint.
    No se si has probado TypeMock.net, es la única librería que consigue crear mocks de métodos no virtuales, clases cerradas ( sealed ) y demás.
    Si con eso no lo logras yo abandonaría la idea. Suerte !

  2. Hola Dan,
    Mucho, mucho dolor… las lagrimas son invisibles en Internet, pero el teclado del computador está inundado los últimos días… como sabes, esa es la vida de un desarrollador: sufrir y seguir sufriendo… y por lo que nos pagan, es mejor que nos cambiemos lo más rápidamente posible al lado oscuro del IT, y nos volvamos Project Managers 😎
    Y ahora en serio, gracias por los tips. Más abajo he copiado un E-mail que he recibido de Eli Lopian (http://www.elilopian.com/) con algunos otros tips y trucs. Me parece que le voy a dar otra oportunidad a los Mocks…
    Un saludo,
    Gustavo

  3. Información recibida de Eli Lopian (http://www.elilopian.com/), que aporta algo más a la discusión:

    —-

    Gustavo, This is a nice post, here are some tips.

    Unlike Mocks/Stubs that Martin Fowler talks about. Using TypeMock, there is no need to pass the stub to the test method.
    so to test this code:
    SPSite siteCollection= SPControl.GetContextSite(Context);

    You can use this (without passing any object):
    Mock mockControl = MockManager.Mock();
    mockControl.AlwaysReturn(“GetContextSite”,mockedSite);

    and to mock this (a future instance!)
    SPQuery query = new SPQuery();
    use
    Mock mockControl = MockManager.Mock();

    Note 1:
    These statments:
    MockObject myLinkA = MockManager.MockObject(typeof(SPLink));
    SPLink myLink = (SPLink)myLinkA.Object;MockObject

    for concrete types is nearly the same as
    MockObject myLinkA = MockManager.Mock();
    SPLink myLink = new SPLink(); // default values

    Note 2: For most Stubs you should use the MockAll and ReturnAlways/ExpectAlways.

    So to stub a property use:
    myListA.ExpectGetAlways(“Title”, “Hola Gustavo”);

    If you get strange behavior (NullReferenceException) try not mocking the Constuctor (Constructor.NotMocked) and don’t try to create the object yourself (using MockObject), but just catch the next instance (using Mock as shown above).

    Also, When you are not mocking your types, use the TypeMock Tracer, this will give you information about what was called and give you an idea where the mock mistake is (for example the constructor was mocked)

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *