Primeros pasos
Instalación
El core framework está disponible como una dependencia típica de Maven
<dependency>
<groupId>com.iconsolutions.test</groupId>
<artifactId>test-fw-core-all</artifactId>
<version>${latest-version}</version>
</dependency>
test-fw-core-all es un módulo agregador para todos los subcomponentes, pero es totalmente posible, y a veces deseable, especificar componentes core individuales.
Pasos Gherkin
Lo primero que hay que entender al usar el test-framework es que escribimos historias BDD usando la sintaxis Gherkin para describir el comportamiento de nuestro sistema y comprobar que actúa como se espera. El lenguaje Gherkin se mejora aún más procesando toda la entrada a través del Expression Engine, lo que a su vez da acceso al contexto del escenario actual, a todos los messages previos dentro del escenario, a cualquier función personalizada registrada y a métodos de beans.
La mayoría de las operaciones de enviar, recibir y verificar mensajes se satisfacen con la biblioteca de pasos en 'CommonTransportSteps'; siguen un patrón extremadamente general de:
Patrones
Patrones básicos
El 99% de todas las acciones dentro de una prueba se puede desglosar en 5 categorías principales, y la mayoría de estas son orquestadas mediante la aplicación core de test-fw, delegando la implementación a implementadores mediante inyección de dependencias y hooks del ciclo de vida.
-
Create: Necesitamos decirle a test-fw cómo crear un Message del tipo deseado. Esto se hace proporcionando una implementación de Generator al MessageDefinition
-
Send: Necesitamos decirle a test-fw cómo enviar un Message del tipo deseado. Esto se hace creando y vinculando un Transport de soporte
-
Receive: Es probable que necesitemos poder consumir messages de este tipo, típicamente registrando JMS Consumers o sondeando un servidor HTTP simulado, y luego añadiendo los messages deserializados en el Message Bucket.
-
Correlate: Los messages se añaden al Bucket entre bastidores, continuamente y de forma asíncrona, a menudo necesitamos poder encontrar nuestro message "pescándolo". Esto se hace suministrando un Predicate a uno de los métodos de acceso de MessageBucket. Como el 99% de las veces el predicado es el mismo para un tipo de mensaje dado, hemos creado el constructo CorrelationStrategy que se puede proporcionar al MessageDefinition. Esto permite que el framework extraiga un predicado según sea necesario; para casos negativos/de borde es preferible construir un Predicate manualmente.
-
Verify: Una vez que hemos encontrado un message correlacionado, probablemente queramos verificar su contenido para comprobar que los valores son correctos. Esto se hace creando una implementación de VerificationService. Un bean de este tipo se autoinyectará automáticamente en CommonTransportSteps y se invocará para ANY message que sea recibido y correlacionado. También es posible registrar una implementación con el MessageDefinition, que se invocará automáticamente pero solo para el Message Type actual.
Enviar un mensaje
-
Determina/encuentra un Document Type adecuado que pueda representar el contenido de tu mensaje.
-
Crea un Message Type correspondiente para que puedas referirte a él en BDD.
-
Asocia el nuevo Message Type con un protocolo de Transport adecuado, utilizando la clase de asociación MessageTransport.
-
Configura y registra un Message Definition asociado (para más información sobre ejemplos de definiciones de mensajes, consulta la documentación de test-fw-ipf)
Una vez completado, deberías poder referenciar tu nuevo tipo de mensaje en un paso BDD. Por ejemplo, el siguiente paso debería ser suficiente para enviar el mensaje al sistema objetivo:
When the 'test system' sends a 'test message'
Qué ocurre entre bastidores:
-
'test message' se interpreta y se recupera la instancia adecuada de Message Type.
-
Se recupera Message Definition mediante búsqueda por Message Type.
-
Message Definition proporciona un generador para generar una nueva instancia de Message.
-
Se identifica un Transport adecuado a partir del Message Type y tanto Message como Message Definition se pasan al Transport.
-
El Transporter serializa el Message a un String usando el toStringMapper configurado en MessageDefinition y lo envía por la red.
La Message Definition
La implementación de cómo construir, enviar, recibir, correlacionar y verificar cada message está completamente definida en la Message Definition; este es un buen ejemplo de Inversión de Control de la aplicación del Test Framework. Cualquier nuevo paso que deba crearse puede implementarse en cualquier componente de Spring que extienda BaseSteps; todos los beans de este tipo en el classpath se cargan en tiempo de ejecución.
Aprovechamos instancias de @AsParameterConverter para interceptar los argumentos de método proporcionados por cualquier paso antes de que lleguen al método Java implementador. Tenemos un convertidor general para tipos String que nos permite aplicar transformaciones/parseo sobre cualquier String mediante el Expression Engine. Hay un convertidor para ParamMap que invoca una lógica similar para cada valor y también proporciona caché con alcance de escenario de los valores evaluados.
Biblioteca de elementos BDD
A continuación se muestran algunos ejemplos de expresiones que pueden usarse comúnmente dentro de un parámetro de cadena para un paso o como un valor en una tabla de ejemplos
-
<aValue> placeholders Diamond: sintaxis estándar de JBehave para usar tablas de ejemplos y reutilizar un valor en pasos. Consulta la documentación de Examples Tables.
-
#context['TX_ID'] - Acceso al contexto: acceso basado en mapa al contexto del escenario
-
#messageType.aProperty.innerProperty[0] - Acceso a message: acceso basado en propiedades a un mensaje manejado previamente (cuando no se especifica la dirección y se han enviado y recibido instancias de ese tipo de mensaje, se predetermina al recibido)
-
#received_messageType_headers['JMS_CORRELATION_ID'] - Acceso a encabezados de message: acceso basado en propiedades al JMS Correlation ID de un message recibido previamente
-
#sent_messageType_meta['QUEUE_NAME'] - Acceso a meta de message: acceso basado en mapa al nombre de la cola de un message enviado previamente.
-
@myBean.randomString(35) - Invocación de método de bean: puede evaluar un método en un bean de Spring disponible; útil para la generación de valores
-
{IS_SET} y {NOT_SET} - Palabras clave de presencia: se manejan y se pueden utilizar para comprobar presencia o ausencia respectivamente.
Etiquetas de metadatos de Story
También hay varias etiquetas de metadatos comunes que se pueden agregar a nivel de story o de escenario que complementan las pruebas.
-
@bankContext <val> - Indicamos si el escenario comprobable es desde el punto de vista de un Originating Bank (OB) o Beneficiary (BB). Esto se utiliza para ayudar con varios comportamientos dependientes del contexto como la mejora de ID y la verificación de colas.
-
@executionHistory <val> - Podemos proporcionar una ruta a un recurso basado en archivo que contiene el historial de ejecución en formato BDD para el flujo esperado en cuestión (esto evita tener 300+ líneas de BDD en cada permutación de escenario probada). Se espera que el valor corresponda a un archivo del mismo nombre con una extensión .eh que exista en cualquier parte del classpath en tiempo de ejecución.
-
@negative <true> - Marca esta prueba como una prueba "negativa", lo que omite el paso de aserción implícito dentro del servicio de verificación común, y se puede aprovechar de manera similar para omitir cualquier comportamiento personalizado que no sea apropiado para pruebas negativas o casos límite.
-
@disableXsdValidation <true> - En la misma línea que negative, se utiliza para determinar si la prueba debe fallar si se viola cualquier validación basada en esquema Object-String configurada. Esto casi siempre se omite, pero puede ser útil para probar el envío de un message estructuralmente inválido.
-
@inprogress (*) - Demuestra que la prueba está en desarrollo y se incluye por igual en el runner de pruebas estándar que se ejecuta como parte de una build. Muchos proyectos también tienen un runner específico de inprogress que ejecutará SOLO estas pruebas.
-
@unparallelisable (*) - Todas las pruebas se ejecutarán concurrentemente por defecto. En la escasa posibilidad de que esto no sea posible debido a probar un recurso compartido, podemos marcar la prueba para que se ejecute en un conjunto separado, de forma secuencial.
Estas etiquetas gobiernan solo los TestRunners, no el contexto del escenario; por lo tanto, solo se necesita su presencia sin ningún valor explícito.