Модульные тесты веб-фреймворка Spark

Я работаю с веб-платформой Spark и создаю RESTful API. (http://sparkjava.com, поскольку существует множество вещей с названием "Spark").

Стандарты моего работодателя требуют, чтобы мы написали серию модульных тестов, которые будут автоматически запускаться один раз в день, чтобы убедиться, что приложения все еще работают.

Spark легко протестировать самостоятельно с помощью такого инструмента, как Postman, но я не нашел хороших примеров JUnit тестов, написанных с помощью Spark или даже с программным выполнением HTTP-запросов с его помощью.

Кто-нибудь делал это раньше? Является ли это возможным?


person Mr Robot Arms    schedule 26.06.2015    source источник


Ответы (4)


У меня было то же требование, что и вы, и я нашли способ заставить его работать. Я просмотрел исходный код Spark и нашел два полезных класса:

  • SparkTestUtil: этот класс обертывает Apache HttpClient и методы предоставления различных HTTP-запросов к локальному веб-серверу (работающему на локальном хосте) с настраиваемым портом (в конструкторе) и относительным путем (в методах запросов)
  • ServletTest: он запускает Экземпляр Jetty в локальном порту с контекстом приложения и относительным путем к каталогу, где можно найти файловый дескриптор WEB-INF/web.xml. Этот файл web.xml будет использоваться для имитации веб-приложения. Затем он использует SparkTestUtil для выполнения http-запросов к этому моделируемому приложению и проверки результатов.

Вот что я сделал: я создал тестовый класс junit, который реализует интерфейс SparkApplication. В этом интерфейсе я создаю и инициализирую «контроллер» (класс моего приложения), отвечающий за ответы на http-запросы. В методе, аннотированном @BeforeClass, я инициализирую экземпляр Jetty, используя файл web.xml, который ссылается на тестовый класс junit как на SparkApplication и SparkTestUtil.

Тестовый класс JUnit

package com.test

import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.webapp.WebAppContext;

public class ControllerTest implements SparkApplication {

    private static SparkTestUtil sparkTestUtil;

    private static Server webServer;

    @Override
    public void init() {
         new Controller(...)
    }

    @BeforeClass
    public static void beforeClass() throws Exception {
       sparkTestUtil = new SparkTestUtil(PORT);
       webServer = new Server();
       ServerConnector connector = new ServerConnector(webServer);
       connector.setPort(PORT);
       webServer.setConnectors(new Connector[] {connector});
       WebAppContext bb = new WebAppContext();
       bb.setServer(webServer);
       bb.setContextPath("/");
       bb.setWar("src/test/webapp/");
       webServer.setHandler(bb);
       webServer.start();
       (...)
    }

    @AfterClass
    public static void afterClass() throws Exception {
       webServer.stop();
       (...)
    }    

}

файл src/test/webapp/WEB-INF/web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <filter>
        <filter-name>SparkFilter</filter-name>
        <filter-class>spark.servlet.SparkFilter</filter-class>
        <init-param>
            <param-name>applicationClass</param-name>
            <param-value>com.test.ControllerTest</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>SparkFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

Это можно улучшить, но я думаю, что это хорошая отправная точка. Может быть, можно создать какой-нибудь компонент "spark-test"?

Надеюсь, это будет полезно для вас!

person Fernando Wasylyszyn    schedule 19.01.2016

мы разработали небольшую библиотеку, которая упрощает модульное тестирование контроллеров/конечных точек Spark.

Github

Также версия 1.1.3 опубликована в Центральном репозитории Maven.

<dependency>
        <groupId>com.despegar</groupId>
        <artifactId>spark-test</artifactId>
        <version>1.1.3</version>
        <scope>test</scope>
    </dependency>
person Fernando Wasylyszyn    schedule 04.02.2016
comment
Последняя опубликованная версия теперь 1.1.1 - person Maks; 24.11.2016
comment
Это круто! Теперь я могу тестировать свои маршруты, не запуская сервер каждый раз. - person Bentaye; 14.02.2017
comment
Это хорошие рамки! Это работает хорошо, но у меня проблемы с запросами, возможно, вы сможете мне помочь. Я выполняю сообщение и сохраняю атрибут в запросе, затем с помощью метода get он возвращает значение null при запросе атрибута в том же @Test. Может сработает или нужно что-то другое делать? - person Motomine; 19.04.2017
comment
@Motomine, что ты имеешь в виду под атрибутом? Как вы его сохраняете? Где? - person Fernando Wasylyszyn; 20.04.2017
comment
Я только что разместил вопрос, связанный с этим здесь, пожалуйста, если вы не не понимаю что-то скажи мне - person Motomine; 20.04.2017
comment
@Motomine Думаю, я понимаю твою проблему. Когда вы выполняете сообщение для входа пользователя в систему, если приложение сохраняет пользователя в сеансе, очень вероятно, что в ответе на сообщение будет возвращен файл cookie с идентификатором сеанса. Вы должны взять этот файл cookie из заголовка ответа на сообщение и поместить его в заголовок запроса на получение isLogged. - person Fernando Wasylyszyn; 21.04.2017
comment
@ferwasy Я пробовал это и получаю карту заголовков, где один ключ — Set-Cookie, а его значение — JSESSIONID=......;Path=/, где в ...... — случайная строка, которая изменения в каждом исполнении. Я уже пытался добавить заголовок с ключом Set-Cookie и этим полным значением, но это не работает. Я делаю что-то неправильно? Я также пробовал использовать JSESSIONID в качестве ключа и случайную строку в качестве значения, и это не сработало. - person Motomine; 21.04.2017
comment
@motormine заголовок в запросе на проверку входа должен быть Cookie, а не Set-Cookie. Последний используется сервером, чтобы указать клиенту, что файл cookie должен быть сохранен. - person Fernando Wasylyszyn; 23.04.2017

Вот мое решение. Вам просто нужно добавить дополнительные зависимости apache-http и junit.

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.2</version>
</dependency>

public class SparkServer {
    public static void main(String[] args) {
        Spark.port(8888);
        Spark.threadPool(1000, 1000,60000);
        Spark.get("/ping", (req, res) -> "pong");
    }
}

public class SparkTest {
    @Before
    public void setup() {
        SparkServer.main(null);
    }
    @After
    public void tearDown() throws Exception {
        Thread.sleep(1000);
        Spark.stop();
    }
    @Test
    public void test() throws IOException {

        CloseableHttpClient httpClient = HttpClients.custom()
                .build();

        HttpGet httpGet = new HttpGet("http://localhost:8888/ping");
        CloseableHttpResponse response = httpClient.execute(httpGet);

        int statusCode = response.getStatusLine().getStatusCode();
        BufferedReader rd = new BufferedReader(
                 new InputStreamReader(response.getEntity().getContent()));

        StringBuffer result = new StringBuffer();
        String line = "";
        while ((line = rd.readLine()) != null) {
            result.append(line);
        }

        assertEquals(200, statusCode);
        assertEquals("pong", result.toString());
    }
}
person songxin    schedule 14.03.2017

Другой подход заключался в создании класса, реализующего Route в каждом path или route. Например, если у вас есть следующий маршрут:

get("maintenance/task", (req, response) -> {....}); 

Затем замените (req, response) -> {....} lambda классом, реализующим Route.

Например:

public class YourRoute implements Route {
   public Object handle(Request request, Response response) throws Exception {
     ....
   }
}

Было бы:

get("maintenance/task", new YourRoute()); 

Затем вы можете выполнить модульное тестирование класса YourRoute с помощью JUnit.


person Pau    schedule 05.10.2017