JMS Commercial
The specmatic-jms module described in this document is available in the Pro plan or higher. Please get in touch with us through the Contact Us form at specmatic.io if you'd like to try it out.
About JMS
Java Message Service (JMS) is a Java API specification for messaging middleware that enables enterprise applications to create, send, receive, and read messages asynchronously. JMS supports both point-to-point (queues) and publish-subscribe (topics) messaging models. It provides features like transactions, message persistence, acknowledgment modes, and message selectors. Common JMS implementations include Apache ActiveMQ, ActiveMQ Artemis, IBM MQ, and WebSphere MQ. JMS is widely used in enterprise Java applications for reliable, transactional messaging.
Using JMS in AsyncAPI Specifications
To define JMS-based messaging in your AsyncAPI specification, you need to declare a server with the jms protocol and reference it in your channels.
Server Definition
servers:
jmsServer:
host: "tcp://localhost:61616"
protocol: "jms"
description: "JMS server"
The host should point to your JMS broker's connection URL. The format varies by implementation (e.g., ActiveMQ uses tcp://, ActiveMQ Artemis may use different schemes).
Channel Definition
channels:
NewOrderPlaced:
address: "new-orders"
servers:
- $ref: "#/servers/jmsServer"
messages:
placeOrder.message:
$ref: "#/components/messages/OrderRequest"
The address field specifies the JMS destination name (queue or topic). Reference the JMS server in the servers array to indicate this channel uses JMS as the transport protocol.
Message Headers
JMS supports rich message properties and headers for metadata:
components:
messages:
OrderRequest:
headers:
type: "object"
properties:
orderCorrelationId:
type: "string"
description: "Unique identifier for the request"
payload:
type: "object"
# ... payload definition
JMS correlation IDs can be set via standard JMSCorrelationID header or custom properties.
JMS Mocking
Specmatic spins up an ActiveMQ server, and expects the system under test to use an ActiveMQ JMS client when running tests. This is both vendor-agnostic and easy to do, given that all JMS clients implement a Java JMS interface.
The crux of the solution is, when running tests, to use the ActiveMQ JMS client and point the application at the ActiveMQ server started by Specmatic. This enables Specmatic to both see the messages sent by the system-under-test and send contract-valid message to the system-under-test.
This document describes how to stub out JMS in applications that use JNDI with Spring Boot, as we have found this to be a common enough case. However, when you can, we recommend that in your tests you disable JNDI and use Spring annotations to instantiate the application's JMS client object.
Pre-requisite Setup
The below-mentioned dependency needs to be in your application's build.gradle or pom.xml
- Maven
- Gradle
<dependency>
<groupId>io.specmatic.jms</groupId>
<artifactId>specmatic-jms-min</artifactId>
<version>0.6.1</version>
</dependency>
implementation("io.specmatic.jms:specmatic-jms-min:0.6.1")
Start the JMS Server
The code below shows how to start the JMS server.
- Java
- Kotlin
JmsMock jmsMock = JmsMock.fromAsyncApiFiles(List.of("src/test/resources/async-api.yaml"), "localhost", 61616);
jmsMock.start();
val jmsMock = JmsMock.fromAsyncApiFiles(listOf("src/test/resources/async-api.yaml"), "localhost", 61616)
jmsMock.start()
If you have the Specmatic Config set up, you can define the specifications in the consumes section and utilize JmsMock.create(host, port) in your test setup to create the JMS mock.
This will start the JMS server running on port: 61616 on localhost.
Set Expectations
The code below shows how to set expectations on the JMS mock. You can specify the number of messages to be received on a specific channel.
- Java
- Kotlin
jmsMock.setExpectations(List.of(new Expectation("product-queries", 2)));
jmsMock.setExpectations(listOf(Expectation(channel = "product-queries", count = 2)))
Verify Expectations
At then end of the test, the code below shows how to check if the expectations have been met. This returns VerificationResult which contains success boolean and errors list of string errors
- Java
- Kotlin
VerificationResult result = jmsMock.verifyExpectations();
assertThat(result.success).isTrue();
assertThat(result.errors).isEmpty();
val results = jmsMock.verifyExpectations()
assertThat(result.success).isTrue
assertThat(result.errors).isEmpty()
You can also verify if a specific ObjectMessage or TextMessage has been received by the JMS mock on a specific channel.
- Java
- Kotlin
assertThat(jmsMock.objectMessageReceivedOnChannel("product-queries", new ProductMessage(1, "Iphone", 10))).isTrue();
assertThat(jmsMock.textMessageReceivedOnChannel("product-queries", "Hello JMS")).isTrue();
assertThat(jmsMock.objectMessageReceivedOnChannel("product-queries", ProductMessage(1, "Iphone", 10))).isTrue
assertThat(jmsMock.textMessageReceivedOnChannel("product-queries", "Hello JMS")).isTrue
Stop the JMS Server
The code below shows how to shut down JMS mock.
- Java
- Kotlin
jmsMock.stop();
jmsMock.stop()
Inject an ActiveMQ JMS client using JNDI
Create a new TestInitialContextFactory.java file into src/test/jms package.
This will create an ActiveMQ server with which clients can interact.
Locate the .properties file and change the value of the JMS endpoint spring.jms.jndi-name or spring.datasource.jndi-name to jms.TestInitialContextFactory(fully qualified classpath).
On running the application, JMS calls are redirected to the newly created server.
Depending on your context, you may need to additional methods in TestInitialContextFactory.
Running Contract Tests with Specmatic-Async
Specmatic-async enables you to run contract tests against applications using JMS by validating that your service correctly implements the AsyncAPI specification.
For a complete working example of JMS contract testing with Specmatic-Async, refer to the specmatic-async-sample project.
Step 1: Configure specmatic.yaml
Create or update your specmatic.yaml file to include the AsyncAPI specification and JMS server configuration:
version: 2
contracts:
- provides:
- specs:
- spec/spec.yaml
specType: asyncapi
config:
servers:
- host: tcp://localhost:61616
protocol: jms
adminCredentials:
username: admin
password: admin
Step 2: Start JMS Infrastructure
Use Docker Compose to start a JMS broker (ActiveMQ Artemis):
services:
artemis:
image: apache/activemq-artemis:latest
ports:
- "61616:61616"
- "8161:8161"
environment:
ARTEMIS_USER: admin
ARTEMIS_PASSWORD: admin
For Apache ActiveMQ Classic:
services:
activemq:
image: apache/activemq-classic:latest
ports:
- "61616:61616"
- "8161:8161"
environment:
ACTIVEMQ_ADMIN_LOGIN: admin
ACTIVEMQ_ADMIN_PASSWORD: admin
Start the infrastructure:
docker compose up -d
Step 3: Run Your Application
Start your application that implements the AsyncAPI specification. The application should be configured to connect to the JMS broker and listen on the appropriate queues/topics defined in your contract.
Step 4: Run Contract Tests
Execute the contract tests using the Specmatic Docker image:
docker run --network host -v "$PWD/specmatic.yaml:/usr/src/app/specmatic.yaml" -v "$PWD/spec:/usr/src/app/spec" specmatic/enterprise test
Specmatic-async will:
- Read the AsyncAPI specification and identify JMS channels
- Connect to the configured JMS broker
- Send test messages to your application's input queues/topics
- Validate that your application sends correct responses to output queues/topics
- Verify that message payloads, headers, and correlation IDs conform to the contract
Step 5: Review Test Results
Check the test output for detailed validation results, including any contract violations or failures.
Tips for JMS Testing
- Broker Selection: Choose the JMS broker that matches your production environment (Artemis, ActiveMQ, IBM MQ, etc.)
- Connection Factory: Ensure the connection factory configuration matches your broker type
- Transactions: Consider enabling transacted sessions for test reliability
- Durable Subscriptions: Use durable subscriptions for topics if message persistence is required
- Message Selectors: Leverage JMS message selectors for filtered message consumption
- JMSCorrelationID: Use the standard JMSCorrelationID header for request-reply patterns
- Acknowledgment Modes: Choose appropriate acknowledgment mode (AUTO, CLIENT, DUPS_OK) for test scenarios
- Temporary Queues: Use temporary queues for reply patterns to avoid queue cleanup issues
- Connection Pooling: Configure connection pooling for better performance in high-volume tests
Sample Applications
Please have a look at the following sample projects to understand how to utilize Specmatic-JMS in your application:
- specmatic-async-sample - Sample project demonstrating JMS contract testing with Specmatic-Async
- specmatic-order-bff-jms - Sample project for JMS mocking