Java Client (JMS Send and JMS Consume) to interact with JMS queues from karate.
It provides features to send and consume JMS Messages (TextMessage or ObjectMessage) for a Queue:
-
How to set it up:
-
Define (if applicable) the JMS client dependencies in the project POM
-
Define (if applicable) the JMS Object Classes in the project POM
-
Define the JMS client configuration in the file src/test/resources/config/jms/<jms-provider>-<env>-config.yml
-
-
How to use it in the karate files:
The Supported JMS Providers are:
POM Configuration
POM Karate Tools
| If the project has been generated using the Karate Tools Archetype the pom will already contain the corresponding configuration. |
Add the karatetools dependency in the karate pom:
<properties>
...
<!-- Karate Tools -->
<karatetools.version>X.X.X</karatetools.version>
</properties>
<dependencies>
...
<!-- Karate Tools -->
<dependency>
<groupId>dev.inditex.karate</groupId>
<artifactId>karatetools-starter</artifactId>
<version>${karatetools.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
POM Client Factory
Apache Active MQ POM
karatetools-starter already includes the Active MQ JMS dependencies.
If you need to change the dependency version, you can include it in the pom as follows:
<properties>
...
<!-- Karate Clients -->
<!-- Karate Clients - JMS -->
<!-- Karate Clients - JMS - ActiveMQ -->
<artemis-jms-client.version>X.X.X</artemis-jms-client.version>
</properties>
<dependencies>
...
<!-- Karate Clients - JMS -->
<!-- Karate Clients - JMS - ActiveMQ -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-jms-client</artifactId>
<version>${artemis-jms-client.version}</version>
</dependency>
</dependencies>
Rabbit MQ POM
karatetools-starter already includes the Rabbit MQ JMS dependencies.
If you need to change the dependency version, you can include it in the pom as follows:
<properties>
...
<!-- Karate Clients -->
<!-- Karate Clients - JMS -->
<!-- Karate Clients - JMS - RabbitMQ -->
<!-- 2.X.X : Version for JMS 2.0 -->
<rabbitmq-jms-client.version>X.X.X</rabbitmq-jms-client.version>
</properties>
<dependencies>
...
<!-- Karate Clients - JMS -->
<!-- Karate Clients - JMS - RabbitMQ -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>rabbitmq-jms-client</artifactId>
<version>${rabbitmq-jms-client.version}</version>
</dependency>
</dependencies>
POM JMS Object
The JMS Client is cappable of processing JMS TextMessage or ObjectMessage.
In order to process JSM ObjectMessages you might need to add in the pom.xml the dependency to the module with the JMS Object classes.
-
POM Properties
<properties> ... <!-- Karate Clients - JMS - JMS Objects --> <xxxxxx-jms-module.version>X.X.X</xxxxxx-jms-module.version> ... </properties> -
POM Dependencies
<dependencies> ... <!-- Karate Clients - JMS - JMS Objects --> <dependency> <groupId>com.mycompany.api</groupId> <artifactId>xxxxxx-jms-module</artifactId> <version>${xxxxxx-jms-module.version}</version> </dependency> ... </dependencies>
Client Configuration
Configuration parameters for the JMS Clients. These values can be overwritten by the corresponding system properties. System properties can be injected by CI/CD.
| If the project has been generated using the Karate Tools Archetype the archetype would have prompted for the creation of the configuration files. |
This client can be configured for multi-environment execution with a config file per environment:
\---src
\---test
\---resources
\---config
\---jms
<provider>-config-local.yml
...
<provider>-config-pre.yml
For example:
\---src
\---test
\---resources
\---config
\---jms
activemq-config-local.yml
...
activemq-config-pre.yml
Apache Active MQ Config
⚙️ jmsFactory - JMS Factory to use: ActiveMQ
⚙️ brokerURL - URL of the Active MQ broker
⚙️ username - User name that the application uses to connect to ActiveMQ
⚙️ password - Password that the application uses to connect to ActiveMQ
⚙️ sendTimeout - Timeout in milliseconds for sending messages (default 5000)
# docker-compose environment variables: <AMQ_USER> <AMQ_PASSWORD>
# docker-compose folder structure: NA
jmsFactory: ActiveMQ
brokerURL: tcp://localhost:61616
username: karate
password: karate-pwd
Rabbit MQ Config
⚙️ jmsFactory - JMS Factory to use: RabbitMQ
⚙️ host - RabbitMQ server host
⚙️ port - RabbitMQ server port
⚙️ username - User name that the application uses to connect to RabbitMQ
⚙️ password - Password that the application uses to connect to RabbitMQ
⚙️ virtual-host - virtual host to be used when creating a connection to RabbitMQ (default is "/")
⚙️ on-message-timeout - Timeout in milliseconds for processing messages (default 5000)
⚙️ amqp - When true, uses native AMQP destinations (default exchange routing) instead of JMS queues. ObjectMessage is not supported in AMQP mode - use Map or String payloads instead (default is false)
# docker-compose environment variables: <RABBITMQ_DEFAULT_USER> <RABBITMQ_DEFAULT_PASS> <RABBITMQ_DEFAULT_VHOST>
# docker-compose folder structure: NA
jmsFactory: RabbitMQ
# host: RabbitMQ server host.
host: localhost
# port: RabbitMQ server port.
port: 25672
# username: User name that the application uses to connect to RabbitMQ.
username: karate
# password: Password that the application uses to connect to RabbitMQ.
password: karate-pwd
# virtual-host: virtual host to be used when creating a connection to RabbitMQ.
virtual-host: /
# on-message-timeout: Timeout in milliseconds for processing messages.
on-message-timeout: 5000
Client Features and Usage
Instantiate JMSClient
New instance of the JMSClient providing the configuration as a map loaded from a yaml file.
public JMSClient(final Map<Object, Object> configMap)
# public JMSClient(final Map<Object, Object> configMap)
# Instantiate JMSClient
Given def config = read('classpath:config/jms/jms-config-' + karate.env + '.yml')
Given def JMSClient = Java.type('dev.inditex.karate.jms.JMSClient')
Given def jmsClient = new JMSClient(config)
Check if JMS is available
Checks if the JMS connection can be established
Returns true is connection is available, false otherwise
public Boolean available()
# public Boolean available()
When def available = jmsClient.available()
Then if (!available) karate.fail('JMS Client not available')
RabbitMQ AMQP mode
The RabbitMQ JMS client operates in two modes:
-
JMS mode (
amqp: false, default): Messages are routed through JMS-managed destinations (e.g.,jms.durable.queuesexchange). ObjectMessage payloads are serialized using Java serialization. This mode is compatible with other JMS clients consuming from the same queues. -
AMQP mode (
amqp: true): Messages are sent as raw AMQP using the default exchange, which routes directly to queues by name. No JMS serialization layer is involved. Only TextMessage and BytesMessage are supported (ObjectMessage will throw aJMSRuntimeException).
Use AMQP mode when the application under test uses native AMQP rather than JMS to interact with RabbitMQ. Common cases include:
-
Spring Cloud Stream applications bound to RabbitMQ - these publish and consume raw AMQP messages, not JMS-serialized ones.
-
Applications using the native RabbitMQ client (
com.rabbitmq:amqp-client) directly. -
Any framework that interacts with RabbitMQ via the AMQP 0-9-1 protocol without a JMS layer.
If the application under test uses the RabbitMQ JMS client (com.rabbitmq.jms:rabbitmq-jms) to produce and consume messages, use JMS mode (the default).
When targeting a Spring Cloud Stream consumer, you must also set the contentType JMS property (e.g., {'contentType': 'application/json'}) so that Spring can deserialize the message payload correctly.
|
Send a message to a queue
Send a message to a queue
For example:
Given def jmsMessageJSON = {'id': '1', 'name': 'karate-01', 'value': 1}
Given def jmsMessagePlainText = 'Plain Text Message'
Given text jmsMessageXML =
"""
<?xml version="1.0"?>
<karate id="1">
<name>karate-01</name>
<value>1</value>
</karate>
"""
In order for an XML to be sent as JMS Text Message you need to use text (with enclosed content """) instead of def (def converts to Map anything that looks like JSON/XML).
|
Given def JMSKarateObject = Java.type('dev.inditex.karate.jms.JMSKarateObject')
Given def jmsMessageObject = new JMSKarateObject("2", "karate-02", 2)
When using RabbitMQ in AMQP mode (amqp: true in config), ObjectMessage is not supported. Use JSON Map, Plain Text, or XML payloads instead. Attempting to send an ObjectMessage in AMQP mode will throw a JMSRuntimeException.
|
public void send(final String queue, final Object value)
# Define Queue
Given def queue = 'global.core.karate.public.queue'
# public void send(final String queue, final Object value)
# Send Message without properties
Given def jmsMessageJSON = {'id': '1', 'name': 'karate-01', 'value': 1}
When jmsClient.send(queue, jmsMessageJSON)
# Define Queue
Given def queue = 'global.core.karate.public.queue'
# public void send(final String queue, final Object value)
# Send Message without properties
Given def jmsMessagePlainText = 'Plain Text Message'
When jmsClient.send(queue, jmsMessagePlainText)
# Define Queue
Given def queue = 'global.core.karate.public.queue'
# public void send(final String queue, final Object value)
# Send Message without properties
Given text jmsMessageXML =
"""
<?xml version="1.0"?>
<karate id="1">
<name>karate-01</name>
<value>1</value>
</karate>
"""
When jmsClient.send(queue, jmsMessageXML)
# Define Queue
Given def queue = 'global.core.karate.public.queue'
# public void send(final String queue, final Object value)
Given def JMSKarateObject = Java.type('dev.inditex.karate.jms.JMSKarateObject')
Given def jmsMessageObject = new JMSKarateObject("2", "karate-02", 2)
When jmsClient.send(queue, jmsMessageObject)
Send a message to a queue with optional properties
Send a message to a queue with defined properties (optional)
For example:
Given def jmsMessageJSON = {'id': '1', 'name': 'karate-01', 'value': 1}
Given def jmsMessagePlainText = 'Plain Text Message'
Given text jmsMessageXML =
"""
<?xml version="1.0"?>
<karate id="1">
<name>karate-01</name>
<value>1</value>
</karate>
"""
In order for an XML to be sent as JMS Text Message you need to use text (with enclosed content """) instead of def (def converts to Map anything that looks like JSON/XML).
|
Given def JMSKarateObject = Java.type('dev.inditex.karate.jms.JMSKarateObject')
Given def jmsMessageObject = new JMSKarateObject("2", "karate-02", 2)
When using RabbitMQ in AMQP mode (amqp: true in config), ObjectMessage is not supported. Use JSON Map, Plain Text, or XML payloads instead. Attempting to send an ObjectMessage in AMQP mode will throw a JMSRuntimeException.
|
When the target consumer is a Spring Cloud Stream application, you must include contentType: 'application/json' in the JMS properties so the message is deserialized correctly. Without it, Spring defaults to application/octet-stream and deserialization fails.
|
# Instantiate JMSClient with AMQP config (amqp: true)
Given def config = read('classpath:config/jms/rabbitmq-amqp-config-' + karate.env + '.yml')
Given def JMSClient = Java.type('dev.inditex.karate.jms.JMSClient')
Given def jmsClient = new JMSClient(config)
# Define Queue
Given def queue = 'my-app.my-input-queue'
# Send JSON message with contentType for Spring Cloud Stream
Given def jmsMessage = {'id': '1', 'name': 'karate-01', 'value': 1}
Given def jmsProperties = {'contentType': 'application/json'}
When jmsClient.send(queue, jmsMessage, jmsProperties)
{ 'PRINT_STATUS': 'PRINTING' }
public void send(final String queue, final Object value, final Map<String, Object> properties)
# Define Queue
Given def queue = 'global.core.karate.public.queue'
# public void send(final String queue, final Object value, final Map<String, Object> properties)
# Send Message with properties
Given def jmsMessageJSON = {'id': '1', 'name': 'karate-01', 'value': 1}
Given def jmsProperties = {'PRINT_STATUS':'PRINTING'}
When jmsClient.send(queue, jmsMessageJSON, jmsProperties)
# Send Message without properties
When jmsClient.send(queue, jmsMessageJSON, null)
# Define Queue
Given def queue = 'global.core.karate.public.queue'
# public void send(final String queue, final Object value, final Map<String, Object> properties)
# Send Message with properties
Given def jmsMessagePlainText = 'Plain Text Message'
Given def jmsProperties = {'PRINT_STATUS':'PRINTING'}
When jmsClient.send(queue, jmsMessagePlainText, jmsProperties)
# Send Message without properties
When jmsClient.send(queue, jmsMessagePlainText, null)
# Define Queue
Given def queue = 'global.core.karate.public.queue'
# public void send(final String queue, final Object value, final Map<String, Object> properties)
# Send Message with properties
Given text jmsMessageXML =
"""
<?xml version="1.0"?>
<karate id="1">
<name>karate-01</name>
<value>1</value>
</karate>
"""
Given def jmsProperties = {'PRINT_STATUS':'PRINTING'}
When jmsClient.send(queue, jmsProperties, null)
# Send Message without properties
When jmsClient.send(queue, jmsMessageXML, null)
# Define Queue
Given def queue = 'global.core.karate.public.queue'
# public void send(final String queue, final Object value, final Map<String, Object> properties)
# Send Message with properties
Given def JMSKarateObject = Java.type('dev.inditex.karate.jms.JMSKarateObject')
Given def jmsMessageObject = new JMSKarateObject("2", "karate-02", 2)
Given def jmsProperties = {'PRINT_STATUS':'PRINTING'}
When jmsClient.send(queue, jmsMessageObject, jmsProperties)
# Send Message without properties
When jmsClient.send(queue, jmsMessageObject, null)
Consume Messages from a queue
Receive the next messages in the queue within the default timeout interval (5000ms) or custom timeout interval.
Returns a JSON Array representing the obtained messages, where each row is a map << field name, message value >>
For example:
[
{ "id": 1, "name": "karate-01", "value": 1 },
{ "id": 2, "name": "karate-02", "value": 2 }
]
[
{ "textMessage": "Plain Text Message" },
{ "textMessage": "<?xml version=\"1.0\"?>\n<karate id=\"1\">\n <name>karate-01</name>\n <value>1</value>\n</karate>" }
]
public List<Map<String, Object>> consume(final String queue)
# Define Queue
Given def queue = 'global.core.karate.public.queue'
# public List<Map<String, Object>> consume(final String queue)
When def messages = jmsClient.consume(queue)
Then karate.log('messages=', messages)
Then assert messages.length == 2
Then def result = karate.sort(messages, x => x.id)
Then match result[0].id == '1'
Then match result[0].name == 'karate-01'
Then match result[0].value == 1
Then match result[1].id == '2'
Then match result[1].name == 'karate-02'
Then match result[1].value == 2
# Define Queue
Given def queue = 'global.core.karate.public.queue'
# public List<Map<String, Object>> consume(final String queue)
When def messages = jmsClient.consume(queue)
Then assert messages.length == 1
Then match messages[0].textMessage == jmsMessagePlainText
# Define Queue
Given def queue = 'global.core.karate.public.queue'
# public List<Map<String, Object>> consume(final String queue)
When def messages = jmsClient.consume(queue)
Then assert messages.length == 1
Then match messages[0].textMessage == jmsMessageXML
Receive the next messages in the queue with custom timeout interval.
public List<Map<String, Object>> consume(final String queue, final long timeout)
# Define Queue
Given def queue = 'global.core.karate.public.queue'
# public List<Map<String, Object>> consume(final String queue, final long timeout)
When def messages = jmsClient.consume(queue, 10000)
Then assert messages.length == 1
Then match messages[0].textMessage == jmsMessagePlainText