О чём статья?
Если вам необходимо автоматизировать тестирование веб-сокетов, то эта статья будет для вас полезна. В ней я поделюсь своим опытом "прикручивания" библиотеки Spring Boot Starter Websocket к проекту автотестов на Java.
По данной теме в интернете можно найти много гайдов, но во всех них приводятся примеры клиентов на JavaScript. Поэтому мне показалось полезным собрать в одном месте необходимые шаги для создания клиента на Java.
Эта статья - своего рода инструкция для быстрого запуска WebSocket-клиента в вашем проекте. Поэтому я намеренно опущу описание ряда деталей (например, что такое веб-сокеты и STOMP-протокол) и подробное описание интерфейсов библиотеки. Вы сможете узнать подробнее о терминах в других источниках, таких как эта статья, и обратиться к документации пакета.
Предисловие
Пара слов о тестируемом мной продукте: это клиентское web-приложение, которое взаимодействует с бэком через http- и ws-протоколы.
Немного о самих автотестах: проект на Java, используется сборщик Maven, тестовый фреймворк TestNG + чистый Selenium, шаблон проектирования тестов PageObject.
Для контроля качества продукта в тестах недостаточно пройти какие-либо пользовательские сценарии от точки А до точки В. Требуется также проверить, что отображаемые на фронте данные соответствуют бизнес-логике и полученным сообщениям от сервера. Для этой цели в автотестах для работы с API обычно используют RestAssured, но данная библиотека не позволяет работать с веб-сокетами. Поэтому мы обращаемся за помощью к Spring Boot Starter WebSocket.
Почему Spring Boot Starter Websocket, ведь есть иные библиотеки для работы с веб-сокетами на Java (например, Java WebSocket)?
1. В интернете по данной библиотеке можно найти больше информации: как минимум, есть документация.
2. В Java WebSocket есть ряд уязвимостей, связанных с зависимостями (например, уязвимость Denial of Service).
Итак, с помощью библиотеки Spring Boot Starter Websocket в проекте автотестов мы создадим клиента. Он будет взаимодействовать с веб-сокетом и выполнять следующие функции:
Подключение к WebSocket (кстати, под капотом библиотеки реализован handshake, поэтому нам не нужно отдельно продумывать update http-запроса до веб-сокета).
Подписка на топик, получение всех сообщений от сервера по веб-сокету.
Отправка сообщений на сервер по открытому веб-сокету.
Отписка от топика.
Закрытие соединения по веб-сокету.
С таким клиентом мы можем получать сообщение от сервера, обрабатывать его и сравнивать полученные данные с отображаемой информацией на фронте.
Step by step...
1. Для создания клиента сначала добавляем библиотеку в проект. Указываем зависимость в pom:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.7.17</version>
</dependency>
2. Создаём два класса:
a) WSClient. В нём будут инициализироваться классы библиотеки, и будет вызываться метод подключения к веб-сокету.
b) StompSessionHandler. Класс содержит описание действий клиента после подключения к веб-сокету.
3. Вызываем метод firstWSClient в самих тестах.
Далее привожу примеры кода для реализации клиента.
Еще оставлю ссылку на примеры из документации.
WSClient
public class WSClient {
public void firstWSClient() {
// Инициализируем классы библиотеки
WebSocketClient webSocketClient = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(webSocketClient);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
StompSessionHandler myStompSessionHandler = new StompSessionHandler();
// Определяем заголовки при обновлении http до ws (handshake)
WebSocketHttpHeaders websocketheaders = new WebSocketHttpHeaders();
// Если требуется передавать авторизационные данные (токен или пароль)
websocketheaders.set("Content-Type", "text/css;charset=UTF-8");
// Определяем заголовок сообщений, передаваемых по веб-сокету
StompHeaders stompHeaders = new StompHeaders();
// Например, указываем топик, на который хотим подписаться
stompHeaders.set(StompHeaders.DESTINATION, "topic/message");
// Указываем URL веб-сокета, к которому требуется подключиться
String URL = "http://localhost:8080/";
// Подключение к веб-сокету
ListenableFuture<StompSession> sessionAsync = stompClient.connect(URL, websocketheaders, stompHeaders, myStompSessionHandler);
StompSession stompSession = sessionAsync.get(5, TimeUnit.SECONDS);
// Подписываемся на топик
stompSession.subscribe(stompHeaders, myStompSessionHandler);
// Оправка сообщения по веб-сокету на сервер
stompSession.send(stompHeaders, "Hello, World!");
}
}
Для работы клиента нужно определить и передать заголовки для http-запроса-«рукопожатия», чтобы открыть веб-сокет. Скорее всего, вам потребуется передать авторизационные данные, например, токен (строки 10-13).
Также определите топик, на который вам нужно подписаться. Это можно выяснить самому, посмотрев подключение через DevTools браузера на вашем фронте. Либо спросите своих backend-разработчиков.
Топик передаём в заголовке сообщения по веб-сокету, на этапе подписки. Само наполнение хедера отражено в строках 15-18.
Далее в примере происходит подключение, подписка и отправка сообщения (строки 23-31). Обращу внимание, что методы subscribe, send перегружены и могут использоваться с другими аргументами. Например, можно не использовать stompHeaders при вызове subscribe, а только передать топик строкой (destination). С методом send такая же картина.
StompSessionHandler
public class StompSessionHandler extends StompSessionHandlerAdapter {
// Описываем действия с подключением к веб-сокету
// В этом примере выводим в консоль, что подключение есть
@Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
System.out.println("Opening websocket...");
System.out.println("Session is connected: " + session.isConnected() + "\n");
}
// Обработчик сообщений из веб-сокета
@Override
public void handleFrame(StompHeaders headers, Object payload) {
String json = new String((byte[]) payload);
System.out.println("Received json:\n" + json + "\n");
}
}
В данном примере метод afterConnected выведет в консоль информацию об успешном подключении к веб-сокету.
Метод handleFrame выводит в консоль сообщения из веб-сокета. С помощью него также можно организовать маппинг сообщений для тестов, где можно будет сравнить полученное по веб-сокету сообщение с данными, собранными через Selenium на фронте.
Инициализация клиента в тестах
@Test(description = "Получение данных по веб-сокету")
public void getWSTestData(){
WSClient wsClient = new WSClient();
wsClient.firstWSClient();
}
Добавляем строки инициализации класса WSClient и вызов метода firstWSClient в нужных тестах, где требуется работа с веб-сокетом.
Заключение
Надеюсь, данная инструкция поможет сэкономить время при создании клиента по работе с WebSocket’ами в ваших проектах по автотестированию на Java. Пусть уровень качества тестируемых продуктов становится только выше!