I have implemented a WebSocket using the api provided with Java EE 7. Additionally I have implemented a client that requests my WebSocket without any problems. To be sure this remains working when performing some code changes I want to implement tests which can also be run on a build server like e.g. Jenkins CI and not only locally. I am using maven.
Here's my server endpoint:
import javax.enterprise.context.ApplicationScoped;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@ApplicationScoped
@ServerEndpoint("/example")
public class WebSocket {
private final Set<Session> sessions = Collections.synchronizedSet(new HashSet<>());
@OnOpen
public void open(Session session) {
sessions.add(session);
}
@OnClose
public void close(Session session) {
sessions.remove(session);
}
@OnError
public void onError(Throwable error) {
//TODO do something
}
@OnMessage
public void handleMessage(String message, Session session) throws IOException {
session.getBasicRemote().sendText("Hello "+message+"!");
}
}
Here's my client endpoint:
import javax.websocket.*;
import java.io.IOException;
import java.net.URI;
@ClientEndpoint
public class WebsocketClient {
private String uri = "ws://localhost:8181/<some-path>/example";
private Session userSession = null;
private String answer;
public WebsocketClient() {
try {
WebSocketContainer container = ContainerProvider
.getWebSocketContainer();
container.connectToServer(this, new URI(uri));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public boolean isClosed() {
return userSession == null || !userSession.isOpen();
}
@OnOpen
public void onOpen(Session userSession) {
this.userSession = userSession;
}
@OnClose
public void onClose(Session userSession, CloseReason reason) {
this.userSession = null;
}
@OnMessage
public void onMessage(String message) {
this.answer = message;
}
public String getAnswer() {
return answer;
}
public void sendMessage(String message) {
if (userSession != null && userSession.isOpen())
try {
this.userSession.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
else {
System.out.println("Session closed!");
}
}
public void reset() {
this.answer = null;
}
}
I also have some other classes that are tested on the build server that use CDI. To be able to use the CDI and other application container functionality I am using the EJBContainerRunner
with apache TomeEE/OpenEJB in version 7.0.0-M3
which starts an embedded TomEE runs my tests and shuts it down afterwards. Using this approach I don't have an URL to call. This works fine with e.g. JAX-RS REST services where you can simply call your classes methods in your tests and check the responses. But how do you do something like this with WebSockets? I can't simply call the methods due to the missing Session
.
My current test looks like this:
import org.apache.openejb.junit.jee.EJBContainerRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.SocketException;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
@RunWith(EJBContainerRunner.class)
public class TestWebsocket {
private WebsocketClient socket;
@Test
public void test() throws SocketException {
String answer = requestSynchronous("Java");
assertThat(answer, is(equalTo("Hello Java!")));
}
public String requestSynchronous(String message) throws SocketException {
if (socket == null || socket.isClosed()) {
socket = new WebsocketClient();
}
socket.reset();
socket.sendMessage(message);
String answer = null;
int i = 0;
while (answer == null && i < 10000) {
try {
answer = socket.getAnswer();
Thread.sleep(1);
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (answer != null)
return answer;
else throw new SocketException("Connection timeout");
}
}
This works running a TomEE on my local machine with e.g. the tomee-maven-plugin
. But this approach can't be done on the build server. Is there a simpler way than configuring special ports or setting up a virtual machine for testing?
Thanks in advance!
Checking websocket for successful connection in JavaScript You can check if a WebSocket is connected by doing either of the following: Specifying a function to the WebSocket. onopen event handler property, or; Using addEventListener to listen to the open event.
Click echo.websocket.org in the Name column, representing the WebSocket connection. Client the Headers tab. This tab shows the WebSocket handshake (upgrade request and response).
I found a solution for my testing problem without mocking, using a vm, ...
For sending an answer message to the client it is not needed to call session.getBasicRemote().sendText("...");
. Instead return the message you want to send.
The handleMessage
method in the class WebSocket
changes to:
@OnMessage
public String handleMessage(String message){
return "Hello "+message+"!";
}
And the test simplifies to:
@RunWith(EJBContainerRunner.class)
public class TestWebsocket {
@Inject
private WebSocket socket;
@Test
public void test() throws SocketException {
String answer = socket.handleMessage("Java");
assertThat(answer, is(equalTo("Hello Java!")));
}
}
This approach doesn't send messages to the outside of the web application container that is started for the test. This makes it much easier to handle the test.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With