2016-04-23 12 views
6

Zaimplementowałem WebSocket za pomocą api dostarczonego z Java EE 7. Dodatkowo zaimplementowałem klienta, który żąda mojego WebSocket bez żadnych problemów. Aby upewnić się, że to działa, gdy dokonujesz pewnych zmian w kodzie, chcę zaimplementować testy, które można również uruchomić na serwerze kompilacji, np. Jenkins CI i nie tylko lokalnie. Używam maven.Jak przetestować Java EE7 Websocket

Oto mój serwer końcowy:

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

Oto mój punkt końcowy klient:

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

Mam też kilka innych klas, które są testowane na serwerze kompilacji, które używają CDI. Aby móc korzystać z CDI i innych funkcji kontenera aplikacji, używam wersji EJBContainerRunner z apache TomeEE/OpenEJB w wersji 7.0.0-M3, która uruchamia wbudowany TomEE, który wykonuje moje testy i zamyka je później. Korzystając z tego podejścia, nie mam adresu URL do połączenia. Działa to dobrze z np. Usługi REST JAX-RS, w których można po prostu wywoływać metody klas w testach i sprawdzać odpowiedzi. Ale jak zrobić coś takiego z WebSockets? Nie mogę po prostu wywołać metod z powodu braku numeru Session.

Mój obecny test wygląda tak:

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

} 

Działa to prowadzenie TomEE na moim komputerze lokalnym z np tomee-maven-plugin. Ale tego podejścia nie można zrobić na serwerze kompilacji. Czy istnieje prostszy sposób niż konfigurowanie specjalnych portów lub konfigurowanie maszyny wirtualnej do testowania?

Z góry dziękuję!

Odpowiedz

6

Znalazłem rozwiązanie mojego problemu testowania bez szyderczy, używając VM ...

uzyskać wysyłając wiadomość odpowiedzi do klienta nie jest potrzebna, aby zadzwonić session.getBasicRemote().sendText("...");. Zamiast tego zwróć wiadomość, którą chcesz wysłać.

metody w klasie handleMessageWebSocket zmiany:

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

a test upraszcza się do:

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

To podejście nie wysyła wiadomości na zewnątrz kontenera aplikacji internetowych, które jest uruchamiany do testu. To znacznie ułatwia obsługę testu.