Las pruebas unitarias te permiten comprobar la calidad del código después de escribirlo, pero también puedes usarlas para mejorar el proceso de desarrollo a medida que avanzas. En lugar de escribir pruebas después de terminar de desarrollar tu aplicación, te recomendamos que las escribas a medida que avanzas. De esta forma, podrás diseñar unidades de código pequeñas, fáciles de mantener y reutilizables. También te permite probar tu código de forma exhaustiva y rápida.
Cuando haces pruebas unitarias locales, ejecutas pruebas que se quedan en tu entorno de desarrollo sin implicar componentes remotos. App Engine proporciona utilidades de prueba que usan implementaciones locales de Datastore y otros servicios de App Engine. Esto significa que puedes probar el uso que hace tu código de estos servicios de forma local, sin desplegarlo en App Engine, mediante stubs de servicio.
A través del código auxiliar de un servicio determinado, se puede simular el comportamiento de ese servicio. Por ejemplo, el stub del servicio de Datastore que se muestra en Escribir pruebas de Datastore y Memcache te permite probar tu código de Datastore sin enviar ninguna solicitud al Datastore real. Las entidades almacenadas durante una prueba unitaria del almacén de datos se guardan en la memoria, no en el almacén de datos, y se eliminan después de la prueba. Puedes realizar pruebas pequeñas y rápidas sin depender de Datastore.
En este documento se proporciona información sobre cómo configurar un framework de pruebas y, a continuación, se describe cómo escribir pruebas unitarias en varios servicios locales de App Engine.
Configurar un marco de pruebas
Aunque las utilidades de prueba del SDK no están vinculadas a ningún framework específico, en esta guía se usa JUnit en los ejemplos para que tengas algo concreto y completo con lo que trabajar. Antes de empezar a escribir pruebas, deberá añadir el archivo JAR de JUnit 4 correspondiente a su classpath de pruebas. Cuando lo hayas hecho, podrás escribir una prueba JUnit muy sencilla.
Si estás usando Eclipse, selecciona el archivo de origen de la prueba que quieras ejecutar. Selecciona el menú Ejecutar > Ejecutar como > Prueba JUnit. Los resultados de la prueba aparecen en la ventana Consola.
Presentamos las utilidades de pruebas de Java 8
MyFirstTest
muestra la configuración de prueba más sencilla posible. En el caso de las pruebas que no dependen de las APIs de App Engine ni de las implementaciones de servicios locales, puede que no necesites nada más. Sin embargo, si tus pruebas o el código que se está probando tienen estas dependencias, añade los siguientes archivos JAR a la ruta de clases de prueba:
${SDK_ROOT}/lib/impl/appengine-api.jar
${SDK_ROOT}/lib/impl/appengine-api-stubs.jar
${SDK_ROOT}/lib/appengine-tools-api.jar
Estos archivos JAR ponen las APIs de tiempo de ejecución y las implementaciones locales de esas APIs a disposición de tus pruebas.
Los servicios de App Engine esperan una serie de elementos de su entorno de ejecución, y la configuración de estos elementos implica una cantidad considerable de código repetitivo. En lugar de configurarlo tú mismo, puedes usar las utilidades del paquete com.google.appengine.tools.development.testing
. Para usar este paquete, añade el siguiente archivo JAR a la ruta de clases de prueba:
${SDK_ROOT}/lib/testing/appengine-testing.jar
Dedica un minuto a consultar el javadoc del paquete com.google.appengine.tools.development.testing
. La clase más importante de este paquete es LocalServiceTestHelper, que gestiona toda la configuración del entorno necesaria y te ofrece un punto de configuración de nivel superior para todos los servicios locales a los que quieras acceder en tus pruebas.
Para escribir una prueba que acceda a un servicio local concreto:
- Crea una instancia de
LocalServiceTestHelper
con una implementación deLocalServiceTestConfig
para ese servicio local específico. - Llama a
setUp()
en tu instancia deLocalServiceTestHelper
antes de cada prueba ytearDown()
después de cada prueba.
Escribir pruebas de Datastore y memcache
En el siguiente ejemplo se prueba el uso del servicio datastore.
En este ejemplo, LocalServiceTestHelper
configura y desconfigura las partes del entorno de ejecución que son comunes a todos los servicios locales, y LocalDatastoreServiceTestConfig
configura y desconfigura las partes del entorno de ejecución que son específicas del servicio de almacén de datos local. Si lees el javadoc, verás que esto implica configurar el servicio de almacén de datos local para que conserve todos los datos en la memoria (en lugar de volcarlos en el disco a intervalos regulares) y borrar todos los datos de la memoria al final de cada prueba. Este es el comportamiento predeterminado de una prueba de almacén de datos. Si no es lo que quieres, puedes cambiarlo.
Cómo modificar este ejemplo para acceder a Memcache en lugar de acceder al almacén de datos
Para crear una prueba que acceda al servicio de memcache local, puedes usar el código que se muestra arriba con algunos pequeños cambios.
En lugar de importar clases relacionadas con el almacén de datos, importa las relacionadas con la caché de memoria. Aún tienes que importar LocalServiceTestHelper
.
Cambia el nombre de la clase que estás creando y la instancia de
LocalServiceTestHelper
para que sean específicos de memcache.
Por último, cambia la forma de ejecución de la prueba para adecuarla a Memcache.
Al igual que en el ejemplo del almacén de datos, LocalServiceTestHelper
y LocalServiceTestConfig
(en este caso, LocalMemcacheServiceTestConfig
) gestionan el entorno de ejecución.
Escribir pruebas de Cloud Datastore
Si tu aplicación usa Cloud Datastore, te recomendamos que escribas pruebas que verifiquen el comportamiento de tu aplicación ante la coherencia final. LocalDatastoreServiceTestConfig
ofrece opciones que facilitan esta tarea:
Si se define el porcentaje de trabajos no aplicados en 100, se indica al almacén de datos local que opere con la máxima coherencia final. La coherencia final máxima significa que las escrituras se completarán, pero siempre fallarán al aplicarse, por lo que las consultas globales (no de ancestros) no podrán ver los cambios. Por supuesto, esto no representa la cantidad de coherencia final que verá tu aplicación cuando se ejecute en producción, pero, a efectos de prueba, es muy útil poder configurar el almacén de datos local para que se comporte de esta forma cada vez.
Si quieres tener un control más detallado sobre las transacciones que no se aplican, puedes registrar tu propio HighRepJobPolicy
:
Las APIs de prueba son útiles para verificar que tu aplicación se comporta correctamente
en caso de que se produzca una coherencia final, pero ten en cuenta que el modelo de coherencia de lectura de alta replicación local es una aproximación del modelo de coherencia de lectura de alta replicación de producción, no una réplica exacta. En el entorno local, al realizar una get()
de un Entity
que pertenece a un grupo de entidades con una escritura no aplicada, los resultados de la escritura no aplicada siempre serán visibles para las consultas globales posteriores. En cambio, esto no sucede en un entorno de producción.
Escribir pruebas de colas de tareas
Las pruebas que usan la cola de tareas local son un poco más complejas porque, a diferencia de Datastore y Memcache, la API de la cola de tareas no expone ninguna función para examinar el estado del servicio. Necesitamos acceder a la cola de tareas local
para verificar que se ha programado una tarea con los parámetros esperados. Para ello, necesitamos com.google.appengine.api.taskqueue.dev.LocalTaskQueue
.
Fíjate en cómo pedimos al LocalTaskqueueTestConfig
un identificador de la instancia del servicio local y, a continuación, investigamos el servicio local en sí para asegurarnos de que la tarea se ha programado según lo previsto. Todas las implementaciones de LocalServiceTestConfig
exponen un método similar. Puede que no siempre lo necesites, pero tarde o temprano te alegrarás de que esté ahí.
Configurar el archivo queue.xml
Las bibliotecas de pruebas de colas de tareas permiten especificar cualquier número de configuraciones queue.xml
por LocalServiceTestHelper mediante el método LocalTaskQueueTestConfig.setQueueXmlPath
. Por el momento, el servidor de desarrollo local ignora la configuración del límite de frecuencia de cualquier cola.
No es posible ejecutar tareas simultáneas a la vez de forma local.
Por ejemplo, un proyecto puede necesitar probar el archivo queue.xml
que se subirá y usará en la aplicación de App Engine. Si el archivo queue.xml
se encuentra en la ubicación estándar, el código de ejemplo anterior se puede modificar de la siguiente manera para conceder acceso de prueba a las colas especificadas en el archivo src/main/webapp/WEB-INF/queue.xml
:
Modifica la ruta del archivo queue.xml
para que se ajuste a la estructura de archivos de tu proyecto.
Usa el método QueueFactory.getQueue
para acceder a las colas por nombre:
Escribir pruebas de tareas diferidas
Si el código de tu aplicación usa tareas diferidas, las utilidades de prueba de Java facilitan la escritura de una prueba de integración que verifica los resultados de estas tareas.
Al igual que en nuestro primer ejemplo de cola de tareas local, usamos un LocalTaskqueueTestConfig
, pero esta vez lo inicializamos con algunos argumentos adicionales que nos permiten verificar fácilmente no solo que la tarea se ha programado, sino que se ha ejecutado. Llamamos a setDisableAutoTaskExecution(false)
para indicar a la cola de tareas local que ejecute las tareas automáticamente. Llamamos a
setCallbackClass(LocalTaskQueueTestConfig.DeferredTaskCallback.class)
para indicar a la cola de tareas local que use una retrollamada que sepa cómo ejecutar tareas diferidas. Por último, llamamos a setTaskExecutionLatch(latch)
para indicar a la cola de tareas locales que disminuya el latch después de cada ejecución de la tarea. Esta configuración nos permite escribir una prueba en la que ponemos en cola una tarea aplazada, esperamos a que se ejecute y, a continuación, verificamos que la tarea se ha comportado como se esperaba cuando se ha ejecutado.
Escribir pruebas de las funciones de los servicios locales
Las pruebas de funciones implican cambiar el estado de algunos servicios, como el almacén de datos, el almacén de blobs, Memcache, etc., y ejecutar la aplicación en ese servicio para determinar si responde como se espera en diferentes condiciones. El estado de la función se puede cambiar mediante la clase LocalCapabilitiesServiceTestConfig.
El siguiente fragmento de código cambia el estado de la función del servicio de almacén de datos a inhabilitado y, a continuación, ejecuta una prueba en el servicio de almacén de datos. Puedes sustituir otros servicios por el almacén de datos según sea necesario.
La prueba de ejemplo primero crea un objeto Capability
inicializado en el almacén de datos y, a continuación, crea un objeto CapabilityStatus
definido como DISABLED. El objeto LocalCapabilitiesServiceTestConfig
se crea con la capacidad y el estado definidos mediante los objetos Capability
y CapabilityStatus
que acabamos de crear.
A continuación, se crea el LocalServiceHelper
usando el objeto LocalCapabilitiesServiceTestConfig
. Ahora que se ha configurado la prueba, se crea el DatastoreService
y se le envía una consulta para determinar si la prueba genera los resultados esperados, en este caso, un CapabilityDisabledException
.
Escribir pruebas para otros servicios
Las herramientas para pruebas están disponibles para el almacén de blobs y otros servicios de App Engine. Para ver una lista de todos los servicios que tienen implementaciones locales para las pruebas, consulta la documentación de LocalServiceTestConfig
.
Escribir pruebas con expectativas de autenticación
En este ejemplo se muestra cómo escribir pruebas que verifiquen la lógica que usa UserService para determinar si un usuario ha iniciado sesión o tiene privilegios de administrador. Ten en cuenta que cualquier usuario con el rol básico de lector, editor o propietario, o con el rol predefinido de administrador de aplicaciones de App Engine, tiene privilegios de administrador.
En este ejemplo, configuramos el LocalServiceTestHelper
con el LocalUserServiceTestConfig
para poder usar el UserService
en nuestra prueba, pero también configuramos algunos datos de entorno relacionados con la autenticación en el propio LocalServiceTestHelper
.
En este ejemplo, vamos a configurar el LocalServiceTestHelper
con el LocalUserServiceTestConfig
para poder usar el OAuthService
.