2016-04-23 1 views
6

J'ai implémenté un WebSocket en utilisant l'API fournie avec Java EE 7. De plus, j'ai implémenté un client qui demande ma WebSocket sans aucun problème. Pour être sûr que cela reste fonctionnel lorsque j'effectue des changements de code, je veux implémenter des tests qui peuvent aussi être exécutés sur un serveur de build comme par ex. Jenkins CI et pas seulement localement. J'utilise maven.Comment tester une Websocket Java EE7

Voici mon point de terminaison du serveur:

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+"!"); 
    } 
} 

Voici mon point de terminaison client:

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; 
    } 
} 

J'ai aussi quelques autres classes qui sont testées sur le serveur de build qui utilisent CDI. Pour pouvoir utiliser le CDI et d'autres fonctionnalités de conteneur d'application j'utilise le EJBContainerRunner avec apache TomeEE/OpenEJB dans la version 7.0.0-M3 qui démarre un TomEE embarqué exécute mes tests et l'arrête ensuite. En utilisant cette approche, je n'ai pas d'URL à appeler. Cela fonctionne bien avec, par exemple, Les services JAX-RS REST où vous pouvez simplement appeler vos méthodes de classes dans vos tests et vérifier les réponses. Mais comment faites-vous quelque chose comme ça avec WebSockets? Je ne peux pas simplement appeler les méthodes en raison du Session manquant.

Mon test actuel ressemble à ceci:

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"); 
    } 

} 

Cela fonctionne l'exécution d'un tomee sur ma machine locale avec par exemple le tomee-maven-plugin. Mais cette approche ne peut pas être effectuée sur le serveur de construction. Existe-t-il un moyen plus simple que de configurer des ports spéciaux ou de configurer une machine virtuelle pour les tests?

Merci d'avance!

Répondre

6

J'ai trouvé une solution pour mon problème de test sans se moquant, en utilisant une machine virtuelle, ...

Pour envoyer un message de réponse au client, il n'est pas nécessaire d'appeler session.getBasicRemote().sendText("...");. Au lieu de cela, renvoyez le message que vous voulez envoyer.

La méthode handleMessage dans la classe WebSocket changements à:

@OnMessage 
public String handleMessage(String message){ 
    return "Hello "+message+"!"; 
} 

et le test simplifie à:

@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!"))); 
    } 
} 

Cette approche ne pas envoyer des messages à l'extérieur du conteneur d'application Web est démarré pour le test. Cela rend beaucoup plus facile de gérer le test.