In a previous article we went through how to build a chat room web application that used REST and STOMP for communicating between the client and server.
In this article I use the very same application and show how to write automated integration tests using the open source Citrus integration test framework.

If you haven’t read the first article don’t worry. A quick summary of all the important bits will be shown shortly below. But before I get to that lets talk a little bit about automated integration testing and citrus.

One of the biggest challenges when testing any application is being able to simulate all endpoints.

If you take an online web shop for example it typically interacts with numerous backend services (product catalogue, credit check, shipping, billing, etc.) during the course of processing the order.
When writing an automated integration test that tests the placement of an order you’ll have to simulate each of these services. Some services may expose a REST/HTTP interface whereas others may expose a SOAP/JMS interface.
In some scenarios the online web shop will be acting as a client, consuming the backend services. In other cases it will be acting as a server, processing customer requests.

The point is that testing such a scenario can be very complex. A simple application today with one or two interfaces may quickly grow into a complex application with 10s or even 100s of interfaces later on.
When you look at integration test tools then don’t loose sight of this. Sure some tools are great at simulating REST interfaces. Others are great at simulating SOAP.
However for me the most important criteria is to find a test tool that combines these and many other messaging protocols. The tool should be flexible and extensible. And this is where citrus comes into the equation.

Before I dive into integration testing, let’s do a quick recap on the chat room application.

Chat Room

The basic architecture of the chat room application is presented below:

We have a client-server architecture, using web sockets and REST for communicating between the client and the server. The system under test is the blue box above and I’m going to write some tests that simulate the two green arrows.

The application’s entity model is very modest, using just the two entities shown below:

A room contains a list of messages and the name of the user that created the room. A message contains some text and the name of the user that sent the message. That’s basically it.

The following REST operations can be sent from the client to the server:

Join or leave the chat application

Get the list of logged in users

Get the list of rooms

Create or remove a room

Send a message to a room

The server can push the following notifications to connected clients using STOMP over WebSocket:

Testing the application

All the source code I’ll be displaying below is available here: https://github.com/martinmaher/jcache-chat-citrus.
I will show you how to build the project from scratch so you can either clone the above repository or just follow the instructions below, whatever you prefer.

The first thing I need to do is to create an empty maven project for storing and executing our tests. The quickest way of doing this is to execute the maven archtetype goal,
which will create a citrus project with a basic pom file and some sample integration tests. This can be done as follows:

The first thing I need to do is to create a new java class for executing my test. I extend the TestNGCitrusTestDesigner class, which allows me to use Citrus’s Java DSL and add I’ll add a variable for storing a random name, which will be used for the user name when joining the chat room.

The first test action sends the request to the HTTP server. The action basically says:

send(“chatRestClient”) -> send using the chatRestClient

payload(“”) -> an empty payload

http() -> using the HTTP protocol

method(HttpMethod.POST) -> using HTTP’s POST method

path(“/users/${username}”) -> to URI to send the HTTP request to

The syntax ${variable-name} is a variable placeholder used in citrus that gets resolved to the value of the variable at runtime. In the example above I use it for adding the variable username to the URI.

The second action is used for verifying that the server processes the request successfully. It expects a HTTP 200 code to be returned from the server.

You may be wondering how citrus knows which host and port to send the request to. It doesn’t, well not yet. I still have to configure this. This strange string chatRestClient is instructing citrus to find an endpoint using this name in the citrus configuration and use this endpoint for sending and receiving requests.

I have added a new HTTP client endpoint chatRestClient that sends requests to the base url http://localhost:8090/. By default the request method POST will be used with the content-type application/json, but both of these can be overloaded in the test. After sending a request citrus will wait 60 seconds for a reply from the server before timing out. And that’s it.

Now go ahead and run this test. If you have the application opened up in your browser then you will notice a new user joins the chat room when the test executes.

To complete the test I’m going to add the test actions for leaving the chat.

Each frame contains a single COMMAND, zero or more HEADERS, a BODY and the null character (^@) to terminate the frame.

In the chat room application the server sends all notifications to connected clients using this protocol. It does this using the WebSocket API since it enables bidirectional communication between the web server and clients. Each time an event occurs, like a user joining the chat room, this event data is then pushed to the clients from the server.

In STOMP there are different types of COMMANDs that can be sent:

CONNECT: this is sent by a client to the server when it connects

CONNECTED: this is an acknowledgement sent by the server to a client on successful connection

SUBSCRIBE_: this is sent by a client to the server to indicate that it is interested in certain types of messages. The client sends a destination header to indicate which messages it is interested in.

MESSAGE_: this is sent by the server to the client when a destination, that the client has subscribed to, sends a message.

DISCONNECT_: this is sent by the client to the server when its disconnecting.

This is by no means an exhaustive list of commands but covers all commands used in the chat room application.

To put all that into perspective, this is basically what happens when the user Joe Blogs joins the chat room application:

The client opens a WebSocket connection to the server

The client sends a CONNECT frame to the server

CONNECT
accept-version:1.1
heart-beat:0,0

The sever sends a CONNECTED frame acknowledgement back to the client

CONNECTED
version:1.1
heart-beat:0,0

The client sends a SUBSCRIBE frame to the server, indicating that it is interested in messages on the destination /topic/users/joined

The first test action is using the endpoint chatWebSocketClient to send the STOMP CONNECT frame to the server. The payload I just covered above only this time control characters like carriage return and the null character have been added.

The second test action is verifying the client was successfully connected. It compares the received payload from the server against the expected payload, which is specified in the test action. Only when both payloads match exactly will the action be successful.

Before I can execute my test I have to configure the chatWebSocketClient endpoint. Again this is done in the citrus-context.xml file by adding the following lines:

There’s nothing special here. I just added a websocket client endpoint that connects to the server on the url ws://localhost:8090/chatEndpoint/websocket.

Now you are good to go and you can run the test. If all goes well you should see a lot of console output with SUCCESS somewhere near the bottom.

Ok, so the test doesn’t really do a whole lot just yet. So I’m going to add the next test actions to subscribe to the destination /topic/users/joined, join the chat room and then finally verify that a STOMP MESSAGE frame is sent to indicate a user has joined.

If you look carefully at the last test action, the MESSAGE frame, you will notice the message-id header. The server sets the value of this header dynamically so the exact payload comparison done here is bound to fail, unless of course you are incredibly lucky. So how do I get around this problem?

Thanks to citrus there are many ways to solve this problem:

Remove the payload verification from the test action - This just ignores the problem for the time being but doesn’t fix it.

Do a partial payload verification – It’s possible to just search for certain strings in the payload, ignoring the rest.

Unmarshall the payload, converting it into a java object and verify the individual attributes – This is slightly more complicated but offers by far the most flexibility.

For the moment I’m going to go with partial payload verification. When using the Citrus Java DSL you can easily do this with a ValidationCallback as shown below:

Great. Go ahead and run the test. This time it should run successfully.

Cleaning up the test

So far I’ve covered a user joining the chat room. I still have to verify that rooms can be created, removed, messages can be sent, notifications for the above are received, etc. But before I do this lets take a step back and look at our test so far.

It’s not bad, but it could be cleaned it somewhat. There are too many concatenated strings, too many carriage returns and other funny characters. It’s too hard to read. It would be great if I could use a fluid API for generating the STOMP frames. And since I’m planning on verifying and extracting data from the received payloads it would be equally cool if this could be somehow unmarshalled. Well guess what, you can do this with citrus. This is what I was talking about earlier when I mentioned how important it is to pick an integration test tool that is flexible. It’s not that citrus supports STOMP out of the box, because it doesn’t, at least not yet. However through its flexible API it enables a tester to add support for features or behaviour that is currently not supported. And this is what I’m going to show you now when I clean up or refactor this test.

I’ll leave the other test class in place and create a new test class Test_03_StompIT, copying over the contents of the original.

I’ll begin by creating a new StompFrame class, which is just a container for holding a STOMP frame in a structured way.

Wrap-up

I added one final test class Test_04_CompleteIT which simulates all REST and STOMP communication in one single test including

subscribing to all STOMP topics,

joining the chat,

creating a room,

sending a message,

deleting a room

and finally leaving the chat

You can find it in my git repository along with all the other tests.

I hope I have inspired you in this blog to at least consider Citrus when you’re thinking about integration tests in the next or even current project. Although I concentrated mainly on testing a web application here, you can use Citrus for just about all integration test scenarios you can imagine. It’s used a lot in middleware applications, which is one of the reasons the test framework was developed to begin with, but it’s not limited to middleware as is hopefully demonstrated above.