2016-04-23 31 views
6

Ich habe einen WebSocket mit der API Java EE 7 implementiert. Zusätzlich habe ich einen Client implementiert, der meinen WebSocket ohne Probleme anfordert. Um sicherzugehen, dass dies funktioniert, wenn einige Codeänderungen durchgeführt werden, möchte ich Tests implementieren, die auch auf einem Build-Server wie z. Jenkins CI und nicht nur lokal. Ich benutze Maven.Wie man einen Java EE7 Websocket testet

Hier ist mein Server-Endpunkt:

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

Hier ist mein Client-Endpunkt:

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

ich auch einige andere Klassen, die auf dem Build-Server getestet werden, die CDI verwenden. Um die CDI- und andere Anwendungscontainer-Funktionalität nutzen zu können, verwende ich die EJBContainerRunner mit Apache TomeEE/OpenEJB in der Version 7.0.0-M3, die ein eingebettetes TomEE startet, führt meine Tests aus und beendet sie anschließend. Mit diesem Ansatz habe ich keine URL zum Anrufen. Dies funktioniert z.B. JAX-RS REST-Dienste, bei denen Sie einfach Ihre Klassenmethoden in Ihren Tests aufrufen und die Antworten prüfen können. Aber wie macht man so etwas mit WebSockets? Ich kann die Methoden wegen der fehlenden Session nicht einfach anrufen.

Mein aktueller Test sieht wie folgt aus:

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

} 

Dies funktioniert eine TomEE auf meinem lokalen Rechner läuft mit z.B. die tomee-maven-plugin. Dieser Ansatz kann jedoch nicht auf dem Build-Server durchgeführt werden. Gibt es einen einfacheren Weg als spezielle Ports zu konfigurieren oder eine virtuelle Maschine zum Testen einzurichten?

Vielen Dank im Voraus!

Antwort

6

fand ich eine Lösung für mein Testproblem ohne spöttisch, ein vm mit ...

Für eine Antwortnachricht an den Client sendet es nicht zu nennen session.getBasicRemote().sendText("..."); benötigt wird. Senden Sie stattdessen die Nachricht zurück, die Sie senden möchten.

Die handleMessage Methode in der Klasse WebSocket Änderungen:

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

Und der Test vereinfacht sich zu:

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

Dieser Ansatz Meldungen des Webanwendungscontainer nach außen nicht senden, wird für den Test gestartet. Dies erleichtert die Durchführung des Tests erheblich.