Inicio > Grails > Probando URL Mappings en Grails

Probando URL Mappings en Grails

Miércoles, 14 de octubre de 2009 chechu Dejar un comentario Ir a comentarios

Llegó el momento de las pruebas, no se pueden posponer más. En Grails existía un plugin que facilitaba mucho esto de las pruebas unitarias y de integración. Facilitaba tanto la labor que a partir de la versión 1.1 decicieron integrarlo en la rama principal de Grails. Y yo que me alegro porque merece la pena.

Son muchas las cosas que podemos probar y Grails nos ayuda en muchos temas específicos proporcionándonos clases de las que extender para desarrollar nuestros tests. Tendremos que dejar nuestras pruebas en los directorios test/integration o test/unit, siguiendo la jerarquía de paquetes de los elementos a probar y heredando de determinadas clases en función de lo que estemos probando:

Para lanzar las pruebas deberemos ejecutar la siguiente orden desde la línea de comandos:

$> grails test-app

En futuros posts iré hablando del resto de pruebas, pero ahora me centraré en las relacionadas con los mapeos de URL’s.

Por defecto los mapeos de URL’s en Grails se declaran en el fichero grails-app/conf/UrlMappings.groovy. Se pueden hacer muchas virguerías, pero eso lo dejamos para otra ocasión. Las pruebas que vamos a desarrollar tienen como objetivo asegurar el correcto funcionamiento de dos procesos que tendrán lugar en nuestra aplicación constatemente:

  1. Extraer de la URL que llega al servidor en una petición los siguientes elementos: controlador, acción y parámetros.
  2. Formar URL’s mediante algunos de los tags proporcionados por Grails, como <g:link> y <g:form>

Es necesario que nuestros tests sean de integración, pues necesitamos que todo el entorno de Grails esté operativo. Al fin y al cabo son sus librerías las que se encargan mágicamente de los dos puntos anteriores. Por lo tanto nuestros tests irán en la carpeta test/integration. Si hemos respetado el nombre del fichero de mapeos usado por defecto, bastará con que coloquemos en este directorio un fichero con el nombre UrlMappingsTest.groovy. En este fichero, todos los métodos que comiencen por test serán considerados pruebas y se ejecutarán una detrás de otra. Los demás métodos se ignorarán. Es posible definir los clásicos métodos setUp y tearDown, que se ejecutarán antes y después de cada prueba respectivamente.

Podemos probar por separado cada uno de los puntos antes comentados, usando el método assertForwardUrlMapping para el primer caso y el método assertReverseUrlMapping para el segundo. Pero en general podemos usar el método assertUrlMapping para ejecutar los dos tipos de prueba.

Utilizaré un ejemplo real de Thuest para ilustrar la explicación. Tenemos los siguientes mapeos definidos:

"/quest/$idText?"{
    controller = "quest"
    action = "show"
    constraints {
        idText(validator:{return !(it in CH.config.theq.urlMappingExceptions.quest)})
    }
}
"/quest/$action?/$idText?"{
    controller = "quest"
}

Con el primer mapeo, para todas las peticiones del estilo /quest/XXX se ejecutará la acción show del controlador quest, almacenando en params.idText el valor XXX como un String. Hay un restricción, según la cual cuando XXX sea alguno de los valores definidos en una lista que tengo declarada en la configuración de Grails (archivo grails-app/conf/Config.groovy), el mapeo no será válido, forzando a Grails a buscar otro mapeo.

El segundo mapeo es más genérico y sirve para capturar URL’s como /quest/listAsPlayer/user1.

La prueba que yo he definido para estos mapeos es la siguiente:

void testQuestMappings() {
    assertUrlMapping("/quest", controller: "quest", action: "show") {
        idText = null
    }
    assertUrlMapping("/quest/test", controller: "quest", action: "show") {
        idText = "test"
    }
 
    // Testing strings that mustn't be idText, because they are actions
    CH.config.theq.urlMappingExceptions.quest.each {action-&gt;
        assertUrlMapping("/quest/${action}", controller: "quest", action:action) {
            idText = null
        }
        assertUrlMapping("/quest/${action}/test", controller: "quest", action:action) {
            idText = "test"
        }
    }
}

Los parámetros que recibe el método assertUrlMapping son:

  • Una URL o un código HTTP (por ejemplo 500 para casos de error o 404 para recursos no encontrados)
  • controller: nombre del controlador en minúsculas
  • action: nombre de una acción
  • view: nombre de una vista
  • paramAssertions: una closure con parámetros que esperaríamos tener en la acción (proceso ForwardUrlMapping) o que proporcioamos a tags como <g:link> y <g:form> para obtener una URL válida (proceso ReverseUrlMapping)

Lo primero que pruebo (líneas 2-4) es que cuando el usuario solicite la URL /quest se ejecutará la acción show del controlador quest, y que el parámetro params.idText será null. Ese caso yo lo tengo contemplado en mi acción, de forma que cuando params.idText no tiene valor hago una redicción a una página por defecto e informo de la situación anormal al usuario.

Al mismo tiempo estoy probando el ReverseUrlMapping, es decir, supongamos que tengo alguno de estos dos tags:

<g:link action="quest" controller="show" params="[idText:null]" />
<g:link action="quest" controller="show" />

Con mi test me aseguro de que la URL generada es /quest. Otra cosa es que esto tenga sentido. En mi caso debo controlar que nunca se genere esta URL porque no tiene sentido, pero si por algún casual mis controles son superados siempre se generará una URL válida.

La siguiente comprobación (líneas 5-7) me asegura que cuando la URL solicitada sea del estilo /quest/XXX se ejecute la acción show del controlador quest y que params.idText tenga el valor XXX.

Igual que en el caso anterior, también se comprueba que el ReverseUrlMapping es correcto, lo cual implica que si tengo el siguiente tag:

<g:link action="quest" controller="show" params="[idText:'mi-quest']" />

La URL que se generará será /quest/mi-quest. Perfecto.

El siguiente fragmento (líneas 10-16) se encarga de comprobar el correcto funcioamiento del mapeo para las excepciones definidas en mi lista de configuración. Estas excepciones se corresponden con todas las acciones que están en el controlador quest y que por tanto tendrán unas URL’s asociadas del estilo /quest/action/parameters. Si no tuviera definidas estas restricciones para el primer mapeo, URL’s como /quest/listAsPlayer/user1 terminarían invocando la acción show del controlador quest, estableciendo en params.idText el valor listAsPlayer.

Algunos imprevistos

Estas cosas no te las encuentras hasta que te pegas con ellas, pero si te puedo quitar un par de horas de curro mejor que mejor.

Error al comprobar el mapeo a un vista

Unos ejemplos típicos serían los siguientes:

1
2
assertUrlMapping(500, view: "error")
assertUrlMapping(404, view: "404")

Lo que se pretende comprobar es que cuando se produce una respuesta HTTP con código 500 la vista que se muestra es grails-app/views/error.gsp. Lo mismo para el código 404 y su vista asociada.

En el momento de escribir este post existe un bug en Grails que impide el correcto funcionamiento: GRAILS-4244. La solución rápida está en el enlace anterior: son necesarios dos import e incluir en el método setUp el siguiente código:

1
2
3
4
5
6
7
8
9
10
import org.springframework.core.io.FileSystemResourceLoader
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
class UrlMappingTests extends grails.test.GrailsUrlMappingsTestCase {
...
    void setUp() { 
        super.setUp() 
        patternResolver = new PathMatchingResourcePatternResolver(new FileSystemResourceLoader())
    }
...
}

Comprobar realmente los parametros esperados en params en el proceso ForwardUrlMapping

Debido a otro bug en Grails, el método assertForwardUrlMapping no comprueba realmente que que el proceso de mapeo deje en el objeto ímplicito params los valores esperados.

La solución pasaría por emplear nuestra propia implementación de la clase grails.test.GrailsUnitTestCase, heredar de ella y modificar el código tal como se recomienda en la página del propio bug.

Terminando

Bueno, me ha salido un poco largo, pero creo que está bien como complemento a la documentación que se puede encontrar por Internet. Espero que te sirva de inspiración para hacer las pruebas de tus mapeos, en realidad son las más sencillas de hacer y te puedes evitar muchas sorpresas.

Categories: Grails Tags:
  1. Sin comentarios aún.
  1. Miércoles, 14 de octubre de 2009 a las 16:17 | #1