Code Segment

Cenando, Buenas Practicas, Historia de terror

Nov 28 2008 by csegura @ 14:36

Cenando

El otro día paso Unai por Pamplona y estuvimos cenando y departiendo sobre todas esas cosas frikis que nos gustan. Una suerte para los que habéis podido asistir a los cursos que ha impartido de Entity Framework  y WCF.

Durante la cena, [[degustábamos unos “Penne Arrabiata” (no seáis mal pensados, que son macarrones con tomate un poco picante) y un “Ochoa Tempranillo” (recomendado)]], hablamos de; libros, de código, de más código, de bugs, de depuración, de tecnología, del bien y del mal (lo típico), no sé si ayer hablamos de sexo (no me acuerdo, pero con el frio que hacía es posible que no :-).

Bueno, la cosa es que le comenté algunas de las cositas del Api de SharePoint.

¿Buenas Prácticas?

Como ya he contado en alguna ocasión, las colecciones de SharePoint, son a la antigua usanza (Net 1.0), es decir que implementan IEnumerable, en una clase contenida, esto lo hacen en una clase abstracta SPBaseCollection del cual heredan el resto de colecciones de SP.

Bien, hasta el momento no hay mucho problema, es un tema de diseño, necesario para implementar las serializaciones de los objetos además de la persistencia que realiza sharepoint via COM.

Si realicemos una consulta usando SPQuery, (SPQuery nos permite recuperar elementos de una lista usando CAML, que es un meta-lenguaje basado en XML que tiene SharePoint para muchas cosas, entre ellas recuperar elementos de las listas)

<Where>
  <Geq>
    <FieldRef Name=”ID/>
    <Value Type=”Integer>10</Value>
  </Geq>
</Where>
Nos devolverá todos los elementos de una lista donde el ID sea mayor que 10.

Para poder ejecutar una consulta CAML sobre una lista tenemos una clase llamada SPQuery a través de la cual montamos la consulta.

SPQuery query = new SPQuery
                {
                   Query = "CONSULTITA EN CAML"
                };

Para recuperar una colección de Items que cumplen esta consulta basta con indicar a la lista los items que queremos pasandole un SPQuery:
SPListItemCollection ítems = lista.GetItems(query);
Bien, como muchos ya sabéis que no es muy buena práctica devolver null, para no tener que comprobar si ítems es null antes de acceder, de modo que lo correcto es devolver una colección vacía (en este caso, como veremos luego tiene otro fundamento).

GetItems internamente nos devuelve una nueva colección
public SPListItemCollection GetItems(SPQuery query)
{
    return new SPListItemCollection(this, query);
} 

Pero qué pasa si la consulta está mal formada ó es incompleta.  Lo que cabe de esperar es:

1.- Que nos devuelva una excepción en el momento de crear SPQuery (cosa que no hace)
2.- Que nos ofrezca una propiedad o algo para comprobar si la consulta es correcta (cosa que no hace)
3.- Que nos devuelva una colección vacía (cosa que hace peligrosamente a medias)

¿Por qué lo hace a medias? Porque aparentemente nos devuelve una colección vacía, es decir si la consulta está mal, ítems no es null. Pero OJO no es una colección valida.

SPQuery query = new SPQuery
                {
                        Query = "MI mala CAML Query"
                };

SPListItemCollection items = list.GetItems(query);

if (items!= null)
{
    Debug.WriteLine("Soy una colección no nula. Parece que valgo...");
    
    // Ahora reviento
    foreach (SPListItem item in items)
    {
    }
}

Las colecciones de este tipo en SharePoint, son de carga retardada de modo que la colección se carga cuando realmente vamos a usarla (Lazy-Load / Proxy), en ese momento se hace una petición SPRequest que es al que se encarga de recuperar el contenido y obtener la colección de elementos. (fundamento)

En ese momento se usa la consulta SPQuery que le hemos pasado para recuperar los datos, y al tratar de cargarla, como la consulta está mal formada, da una excepción (no advertida ni documentada), y la colección sigue siendo una colección hasta que usemos alguna de sus propiedades o tratemos de recorrerla. Es decir no es una colección vacia, si no una colección donde todo se ha quedado mal inicializado si intentas un items.Count antes de recorrer la colección tambien dará una excepción ya esta va a producirse de igual manera al cargar los datos.

De modo que la buena práctica se convierte en mala práctica ya que se pasa de hacer una comprobación de null antes, a tener que hacer un Try/Catch en el momento de recorrer la colección (como decimos por aquí, cojonudo).

Como alternativa, se puede implementar un método por ejemplo, vía extensión;  para comprobar que la consulta es valida y en caso de no serla que nos devuelva una colección bien formada cuando menos. Otra alternativa podría ser otro método como EnsureCollection, a través del cual podamos recuperar la excepción si nos interesa comunicar que la consulta esta mál formada.

La cosa sería algo así:

public static SPListItemCollection TryGetItems(this SPList list, SPQuery query)
{
    SPListItemCollection items; 

    try
    {
        items = list.GetItems(query);
        int count = items.Count;
    }
    catch(SPException ex)
    {
        query.Query = string.Empty;
        items = list.GetItems(query);                
    }
    
    return items;
}

JA, pero ahí no termina la cosa …  los señores de SharePoint son unos fenómenos optimizando y lo que hacen SPQuery internamente es generar una Vista (propiedad ViewXml de SPQuery), para optimizar el rendimiento, esta vista en Xml, se genera reuniendo todas las propiedades de SPQuery, y se genera siempre y cuando no se ha generado antes (optimización)

De modo, que si cambiamos la propiedad Query de SPQuery, la consulta interna ViewXml no cambia (ya se genero antes), manteniendo la última consulta generada, para lo cual debemos limpiar ViewXml.

query.Query = string.Empty;
query.ViewXml = string.Empty;
items = list.GetItems(query); 

¿Por qué que les habría costado implementar un flag de suciedad y reconstruir la ViewXml cada vez que el objeto este sucio? ¿por qué no documentarlo?

En fin, la cena estuvo genial.

Comments (0)

He leido "Code Clean", de Robert C. Martin

Nov 17 2008 by csegura @ 22:16

He terminado de leer el último libro de Robert C. Martin, Clean Code. 

De Robert, había leído otros dos libros, “UML para programadores Java”, una guía práctica y sin rodeos de cómo usar UML en proyectos reales; y cuando digo práctica es que no se anda por las ramas y en ocasiones en un tono incluso “irónico” explica perfectamente cómo debemos usar UML.


El otro libro es Agile Principles, Patterns, and Practices in C# (Robert C. Martin Series), que puedo decir de este libro ampliamente comentado, uno de los que se deben tener.  (Ya sabéis que se lo recomiendo a todo el mundo)

Bueno, de modo que “Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin Series) ” tenía todos los boletos de ser un gran libro. No ha defraudado en absoluto.

Apenas empezarlo, en la introducción ya me había pillado. (Ilstración de la introducción)

http://www.osnews.com/images/comics/wtfm.jpg

El libro explica cómo escribir buen código, código legible y eficiente.  Los ejemplos son en Java, pero tener en cuenta que estamos hablando de cómo escribir buen código, habla del significado de los nombres, de las funciones,  de los argumentos, del tipo de retorno, de cómo comentamos el código, de las reglas de formato, de objetos y estructuras de datos, de la gestión de errores, de las pruebas unitarias, del diseño simple, de sistemas, de concurrencia, de refinamiento, de olores y heurística.  Habla de estilo, de buen estilo.

De veras, me ha parecido un libro impresionante, con unos ejemplos claros, con un estilo cuidado y detallado, sin dejar cosas fuera, insistiendo en las cosas importantes (esto a mí me gusta, así entran mejor las cosas)

Vamos un libro que entra de lleno en mi TOPTEN.

Comments (0)

Resumen del DevCamp en NavarraDotNet

Nov 17 2008 by csegura @ 12:05

Un "poquito" tarde (y con mucha ayudita) os dejo el resumen con todos los detalles del DevCamp en el blog de NavarraDotNet

http://www.navarradotnet.com/post/navarradotnet-en-el-7bdev_camp7d.aspx

Y la información sobre el próximo evento el University Tour

http://www.navarradotnet.com/post/el-University-Tour-viene-a-la-UPNA.aspx 

(Para los que no lo sepan, en la Upna se esta organizando un DotNetClub)

http://www.navarradotnet.com/post/se-esta-cociendo-un-DotNetClub-en-la-UPNA!!.aspx

y como no, siempre vuelve a casa por navidad ... (como el turron)

http://www.navarradotnet.com/post/Chema-Alonso-en-CuatroVientos-Una-sesion-dirigida-a-estudiantes.aspx

Toda la información sobre el paso del Guille por Pamplona (Gracias Guille)

http://www.navarradotnet.com/post/gracias-Guille.aspx

 

Comments (0)

Ya es oficial, SPDisposeCheck está listo

Nov 12 2008 by csegura @ 21:43

De veras, no sé cómo será el lanzamiento de un producto pero este simple programita que permite comprobar que las disposiciones de objetos SPWeb y SPSite se han realizado correctamente, les ha llevado más de 8 meses desde que algunos la suerte de probar las betas.

Las llamadas que realiza la API de SharePoint, reservando memoria COM, sobre todo cuando se abren sitios y webs, no se libera por medio del recolector de basura del CLR, de modo que debemos liberar las reservas manualmente, esto en ocasiones se nos pasa y como no, tenemos que terminar tirando los servicios para recuperar esos recursos y pasar horas buscando los aujeros.

SP DisposeCheck, usa reflexión y comprueba que en nuestros ensamblados se están haciendo los disposes correctos. Esto en un principio podía dar falsos positivos, cosa que se ha erradicado prácticamente.

Os recomiendo usar esta herramienta encarecidamente, ya que aunque todo se haga bien, no está de más poder comprobarlo. Así que es una de esas que debemos tener…

Para más información aquí

 

Comments (0)

Testeo Unitario para SharePoint: acercandose a la respuesta definitiva - parte 1

Nov 3 2008 by csegura @ 22:04

Como sabéis tanto Gustavo como yo llevamos tiempo realizando pruebas sobre lo que programamos en SharePoint, ambos estamos usando TypeMock como framework para nuestros objetos Mocks que son imprescindibles a la hora de realizar pruebas para SharePoint.

Durante los últimos meses hemos posteado sobre el tema, hemos iniciado un proyecto en CodePlex con un conjunto de clases envoltorio que pueden ayudarnos a la hora de realizar las pruebas con TypeMock y que está a un tris de quedarse obsoleto.

Antes de continuar me gustaría hacer una pequeña recapitulación sobre algunos puntos y características que nos ofrece TypeMock a la hora de realizar pruebas para SharePoint.

Veamos un sencillo ejemplo, para probar el código siguiente:

   1:  public Guid SampleGetSiteID()
   2:  {
   3:       Guid siteID = Guid.Empty;
   4:   
   5:       using(SPSite site = new SPSite("http://moss"))
   6:       {
   7:           siteID = site.ID;
   8:       }           
   9:   
  10:       return siteID;
  11:  }

En este código estamos usando un objeto SPSite que no podemos sustituir a priori de modo que de cara a probar esto es un problema.

TypeMock, lo resuelve permitiéndonos crear simulaciones de clases de objetos antes de que estas se usen, después TypeMock, sustituirá la instancia real con una instancia de la clase simulada que hemos creado.

Para probar el código anterior podemos hacer lo siguiente:

   1:  [TestMethod]
   2:  public void SampleGetSiteID_ReflectiveMock()
   3:  {
   4:     MockManager.ClearAll();
   5:     MockManager.Init();
   6:   
   7:     Guid expectedId = Guid.NewGuid(); 
   8:   
   9:     Mock<SPSite> mockFutureInstanceOfSPSite = MockManager.Mock<SPSite>(Constructor.Mocked);
  10:     mockFutureInstanceOfSPSite.ExpectGetAlways("ID", expectedId);
  11:   
  12:     Samples target = new Samples();
  13:     Guid resultId = target.SampleGetSiteID();
  14:   
  15:     Assert.AreEqual(resultId, expectedId);
  16:  }


Mock<SPSite> mockFutureInstanceOfSPSite = MockManager.Mock<SPSite>(Constructor.Mocked) se encarga de mockear (simular) la clase SPSite al usar Mock<SPSite> estamos definiendo una clase simulada que será reemplazada en el momento de su uso (nos estamos anticipamos).

Vemos también como hemos fijado la propiedad mockFutureInstanceOfSPSite.ID por medio de mockFutureInstanceOfSPSite.ExpectGetAlways("ID", expectedId); para que devuelva expectedId.

En el momento en que se ejecuta la prueba TypeMock se encargará de sustituir la solicitud new SPSite("http://wsses") con nuestro mockFutureInstanceOfSPSite. Y cuando se solicite la propiedad ID del SPSite se devolverá el valor que hemos establecido para la propiedad "ID" en la expectativa.

Esta propiedad se establece de forma reflectiva (TypeMock usa reflexión para establecer este valor), de esta forma también podemos crear simulaciones de clases de objetos que tienen constructores privados así como establecer campos "privados ó internos" de la clase que estamos mockeando (simulando); De esta característica surge el nombre de "Reflective Mocks"

Pero aún hay más, en este caso hemos creado la simulación, y le hemos establecido una expectativa, esto es que lo que esperamos realmente es que exista una llamada a la propiedad ID del SPSite.

Para comprobar que esa expectativa se ha cumplido, es decir que realmente se ha hecho la solicitud, podemos añadir el atributo [VerifyMocks] a nuestra prueba o bien añadir MockManager.Verify() al finalizar la prueba.

Con esto TypeMock, comprobara que efectivamente se ha hecho la llamada.

Para verlo funcionar podemos establecer:

mockFutureInstanceOfSPSite.ExpectGet("HostName", "MyHost");

Y como resultado TypeMock

Test method TestProject1.TestSamples2.SampleGetSiteID_ReflectiveMock threw exception: TypeMock.VerifyException:
TypeMock Verification: Method Microsoft.SharePoint.SPSite.get_HostName() has 1 more expected calls


De modo que podemos verificar que nuestro método no ha llamado a HostName como esperábamos.

No se vayan todavía aún hay más

TypeMock tiene una particularidad que creo que ya he comentado en más de una ocasión que es la de poder simular clases de objetos que tienen un constructor privado. Y esto lo hace excepcional. Sobre todo para SharePoint.

Natural Mocks

Por medio de los "Natural Mocks" podemos definir un conjunto de expectaciones, grabarlas y después comprobar si se han llevado a cabo. (Si, esto ya se podía hacer antes) El detalle aquí es que a la hora de fijar las expectativas estas se definen de manera natural pero solo dentro de un Recorder, que será el encargado de grabar dichas expectativas.

Vemos como sería la misma prueba usando Natural Mocks


   1:  [TestMethod] 
   2:  public void SampleGetSiteID_NaturalMock()
   3:  {
   4:      MockManager.ClearAll();
   5:      MockManager.Init();
   6:   
   7:      Guid expectedId = Guid.NewGuid();
   8:   
   9:      using (RecordExpectations recorder = RecorderManager.StartRecording())
  10:      {
  11:          SPSite mockFutureInstanceOfSPSite = new SPSite(string.Empty);
  12:          recorder.ExpectAndReturn(mockFutureInstanceOfSPSite.ID, expectedId); 
  13:      }
  14:   
  15:      Samples target = new Samples();
  16:      Guid resultId = target.SampleGetSiteID();
  17:   
  18:      Assert.AreEqual(resultId, expectedId);
  19:  }


Como puede apreciarse ahora las expectativas las fijamos sobre el recorder y la sintaxis es natural, ya no necesitamos fijar las expectativas de las llamadas a propiedades ó métodos usando strings, podemos usar directamente las propiedades aprovechando el intellisense y la corrección de errores;

El otro cambio es que estamos creando una instancia natural de SPSite dentro del recorder; TypeMock la usará luego para la sustitución. Como es obvio, es más Natural.

Como inconveniente, en los mocks naturales no podemos fijar expectativas para los campos "privados o internos".

Bien, hemos empezado con algo que a parecía complejo por el hecho de tener que sustituir un objeto (SPSite) que se encuentra dentro de un método (SampleGetSiteID) y hemos visto como TypeMock nos permite realizar esa sustitución.

Evidentemente el método (SampleGetSiteID) está fuertemente acoplado a la API de SharePoint, cosa que debemos evitar en la medida de lo posible. Como es lógico esto se ha hecho con el propósito de ejemplo.

Ahora veremos cómo podemos usar las instancias de los objetos simulados en nuestras pruebas.

El código a probar sería el siguiente:

   1:  public int SampleGetWebsInSite(SPSite site)
   2:  {
   3:      int countWebsInSite;
   4:   
   5:      using (SPWeb web = site.OpenWeb())
   6:      {
   7:          countWebsInSite = web.Webs.Count;
   8:      }
   9:              
  10:      return countWebsInSite;
  11:  }  

En este caso nuestro método necesita un SPSite de modo que para probarlo necesitaremos una instancia de SPSite; Pero SPSite es un objeto un poquito raro, y requiere de una conexión real y de toda la infraestructura de SharePoint para poder instanciarse.

En este caso podemos usar las instancias de los objetos mockeados.

Primero veamos cómo hacerlo usando "Reflective Mocks"


   1:  [TestMethod]
   2:  public void SampleGetWebsInSite_Test_Reflective()
   3:  {
   4:      MockManager.ClearAll();
   5:      MockManager.Init();
   6:   
   7:      MockObject<SPSite> mockSPSite = MockManager.MockObject<SPSite>(Constructor.Mocked);
   8:      MockObject<SPWeb> mockSPWeb = MockManager.MockObject<SPWeb>(Constructor.Mocked);
   9:      MockObject<SPWebCollection> mockSPWebCollection = MockManager.MockObject<SPWebCollection>(Constructor.Mocked);
  10:   
  11:      SPWeb webInstance = mockSPWeb.MockedInstance;
  12:      SPWebCollection webCollectionInstance = mockSPWebCollection.MockedInstance;
  13:   
  14:      mockSPSite.ExpectAndReturn("OpenWeb", webInstance);
  15:      mockSPWeb.ExpectGet("Webs", webCollectionInstance);
  16:      mockSPWebCollection.ExpectGet("Count", 5);
  17:   
  18:      Samples target = new Samples();
  19:      int websInSite = target.SampleGetWebsInSite(mockSPSite.MockedInstance);
  20:   
  21:      Assert.AreEqual(websInSite, 5);
  22:      MockManager.Verify();
  23:  }


En este caso, lo que hacemos es crear un MockObject de SPSite, un MockObject de SPWeb, cuya instancia real (mockFutureInstanceOfSPWebCollection.MockedInstance) devolveremos cuando se solicite OpenWeb, un MockObject de SPWebCollection, cuya instancia devolveremos cuando se solicite la colección de webs dentro del sitio y finalmente establecemos el número de elementos que la colección falsificada debe devolver que es 5.

Como es lógico y se aprecia en el código anterior, podemos ver que el orden a la hora de crear los distintos elementos es muy importante para que todo encaje perfectamente.

Una cosa más, fijaros que estamos usando MockObject<SPSite> en vez de Mock<SPSite> esto es debido a que no necesitamos crear el objeto con anterioridad, si no que lo vamos a usar directamente en la llamada a nuestro método.

target.SampleGetWebsInSite(mockSPSite.MockedInstance);

Bien, ahora veremos cómo podemos realizar la misma prueba usando

Natural Mocks

   1:  [TestMethod]
   2:  public void SampleGetWebsInSite_Test_NaturalMock()
   3:  {
   4:      MockManager.ClearAll();
   5:      MockManager.Init();
   6:   
   7:      SPWeb web = MockManager.MockObject<SPWeb>().MockedInstance;
   8:      SPWebCollection webs = MockManager.MockObject<SPWebCollection>().MockedInstance;
   9:   
  10:      using (RecordExpectations recorder = RecorderManager.StartRecording())
  11:      {
  12:          SPSite site = new SPSite("");
  13:   
  14:          recorder.ExpectAndReturn(site.OpenWeb(), web);
  15:          recorder.ExpectAndReturn(web.Webs, webs);
  16:          recorder.ExpectAndReturn(webs.Count, 5); 
  17:      }
  18:   
  19:      Samples target = new Samples();
  20:      int websInSite = target.SampleGetWebsInSite(new SPSite(""));
  21:   
  22:      Assert.AreEqual(websInSite, 5);
  23:      MockManager.Verify();
  24:  }

Lo primero, es que es menos farragoso, lo que hacemos es crear las falsificaciones para las clases SPWeb y SPWebCollection, ambas las usaremos luego.

Dentro del recorder creamos un objeto SPSite, esto falsificara el primer uso que se haga de SPSite y fijaremos las expectativas devolviendo las instancias reales de las falsificaciones. (Nótese la llamada a MockedInstance)

SPWeb web = MockManager.MockObject<SPWeb>().MockedInstance;

web es una instancia de SPWeb, que se ha creado a partir de la falsificación de SPWeb.

Bien, en el momento de crear nuestra prueba estamos llamando a target.SampleGetWebsInSite creando un nuevo SPSite (new SPSite("") ) ¿fallara?, NO, no falla, como he dicho antes, TypeMock devolverá una instancia de SPSite que es lo que le hemos dicho dentro del recorder y en cada solicitud site.OpenWeb(), Webs y Count devolverá los valores que hemos fijado en las expectativas del recorder.

Bueno, esto es solo una pequeña introducción al tipo de cosas que nos deja hacer TypeMock, como framework de pruebas para SharePoint, hay mucho, mucho más que encontraréis en los manuales de TypeMock.

Bien, en este punto ambos, Gustavo y yo nos encontramos con el problema de recorrer las colecciones de objetos como Webs, Lists, Users etc... de SharePoint;

Esto origino el proyecto de SPTypeMock con las clases envoltorio que nos ayudaban con el tema de las colecciones pero como he dicho antes esto está a puntito de quedarse obsoleto…

Continuara…

Comments (0)