Подтвердить что ты не робот

Выполнение модульных тестов на сервере (JAX-RS)

Я пишу приложение JAX-RS (Jersey + Maven), которое делает некоторые сложные вещи (например, вызывать встроенные исполняемые файлы, встроенные в WAR). Мне нужно запустить некоторые из моих модульных тестов (JUnit4) на сервере (Amazon Elastic Beanstalk с Tomcat 7.0.22), чтобы проверить, что все в порядке.

Есть ли стандартный гибкий способ сделать это, кроме RYO (сворачивать свой собственный)? То, что я нашел, похоже, больше связано с интеграционным тестированием на машине-разработчике (т.е. В тестовой платформе Джерси). Даже RYO меня путает... как я могу вызвать код в тестовых пакетах из исходных пакетов?

В принципе, я хочу создать ресурс /test, который я могу вызвать, который вернет мои результаты unit test с сервера в симпатичном формате. Еще лучше, если я мог бы сделать/test/{category}

4b9b3361

Ответ 1

Я хотел поделиться тем, что узнал после публикации этого вопроса, и выложить свой первый ответ на StackExchange (сайт, на котором я пришел бесчисленное количество раз через Google в поисках решений для моих бесконечных проблем)

Единица против интеграции с континуумом функционального тестирования

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

(полученный запрос) - (функция 1 называется) - (вызов функции 2) - (вызываемая функция 3) - (отклик отправлен)

Единичное тестирование тестирует каждую функцию (или класс или блок) отдельно по отдельности, подавая вход и проверяя вывод. Тестирование интеграции занимает несколько единиц (например, цепочки функций 2-функции 3), а также делает ol-in-and-out. Функциональное тестирование проходит по всей цепочке, от запроса до ответа. Я оставлю его читателю, чтобы угадать некоторые преимущества и недостатки тестирования на каждом уровне масштаба. Во всяком случае, ВСЕ ЭТИ ИСПЫТАНИЯ МОГУТ БЫТЬ РАБОТАНЫ В СЕРВЕРЕ, И ЕСТЬ ХОРОШИЕ ПРИЧИНЫ, ЧТОБЫ ИХ ИМУТЬ.

Типы тестирования внутри контейнера/сервера

  • Container-in-the-tests. Функция Spring и другие среды инъекций зависимостей позволяют настроить контейнер, заполненный только минимальными классами (плюс все макеты) для каждого ваших тестов. Это очень удобно, поскольку устраняет необходимость ручной проводки и лучше приближает производственную среду. Это позволяет тестировать только единицы измерения и интеграции.
    • Преимущества: а) традиционное модульное тестирование (с его преимуществами целенаправленных и изолированных тестов) стало более удобным b) ближе к производственной среде, так как вы проверяете логику автоустройства e) интегрируется с тест-листом IDE f) быстро
    • Недостатки: а) окружающая среда может отличаться от производства b) не заменяет необходимость функционального тестирования.
  • Сервер-в-тесте Обычный тестовый бегун запускает почти обычные модульные тесты, которые запускают встроенный сервер или контейнер и вызывают на него вызовы. Несколько фреймворков (например, Framework Testing Framework) допускают только функциональное тестирование, но большинство (Arquillian, jeeunit) позволяют делать все типы. С некоторыми из этих фреймворков это похоже на то, что тесты выполняются на сервере вместе с вашим кодом и могут выполнять любые вызовы.
    • Преимущества (помимо того, что у вас есть доступ ко всем службам контейнера и сервера): а) у вас есть автономные тесты и не нужно устанавливать или настраивать что-либо b) тесты изолированы, потому что для каждого тестового или тестового набора создается новый сервер/контейнер. б) интегрируется с тестируемым IDE
    • Недостатки: а) окружающая среда может отличаться от производства (например, Jetty не является Tomcat или Glassfish) b) запуск/остановка сервера замедляет тесты; c) основы сосать. Jeeunit - это крошечный проект, который даже не был протестирован в Windows. Arquillian большой, но очень новый, плохо документированный, и я не мог заставить его работать.
  • Тесты в сервере. Здесь тесты фактически скомпилированы и запускаются вместе с вашим кодом.
    • Преимущества: а) у вас простые, старые тесты, которые не нужно знать или использовать какие-либо рамки.
    • Недостатки: a) отсутствие изоляции между испытаниями (не обязательно проблема или даже недостаток, но, возможно, необходимо принять меры предосторожности) b) не интегрируется с IDE-тестировщиком (по крайней мере, в Netbeans)
    • Использование Maven во время сборки Maven запускает сервер, загружает ваш специальный тест WAR, выполняет тесты и дает хороший отчет Surefire.
      • Дополнительные преимущества: а) это было сделано во время сборки (и будет интегрироваться с инструментами непрерывной интеграции и другими) b) нет необходимости устанавливать или настраивать что-либо (Maven будет загружать, запускать и т.д. сервер автоматически)
      • Дополнительные недостатки: a) окружающая среда может быть совсем другой (Maven использует Jetty, и он работает на вашей машине) b) не может повторно запускаться в процессе производства
    • тестирование in-WAR. Тесты постоянно компилируются вместе с вашим кодом. Когда бы и где бы ни находилась ваша ВОЙНА, вы можете запустить тесты. На вашем сервере разработки, во время постановки, даже в производстве. Это мой первоначальный вопрос.
      • Дополнительные преимущества: a) ТОЧНО правильная среда. б) запускать тесты всякий раз, когда
      • Дополнительные недостатки: a) необходимо настроить сервер

Там еще один момент. Netbeans дает большую часть преимуществ тестирования Maven для тестирования в WAR. Он включает встроенный сервер и запускается и развертывается автоматически после сборки. Он даже открывает Firefox... просто установите его, чтобы указать на ваш/тестовый ресурс. Это так же, как делать это с Maven, но лучше.

В любом случае, я покажу вам, как проводить тестирование Maven и тестирование в WAR во время одного и того же проекта Maven.

Контейнер в тестах с использованием Spring:

Spring представляет собой растягивающуюся структуру контейнера. Его механизмы впрыска зависимостей переплетаются с Jax-RS славным эффектом за счет значительной кривой обучения. Я не буду объяснять, как работает Spring или Jax-RS. Я перейду в инструкции и, надеюсь, читатели смогут адаптировать идеи к другим сценариям.

Способ получения контейнера, идущего в ваших тестах JUnit 4, - использовать тестовый бегун Spring, объявить классы, которые вы хотите зарегистрировать в контейнере, зарегистрировать некоторые вспомогательные классы Jax-RS, зарегистрировать свои mocks и, наконец, использовать ваш ресурс Jax-RS, как если бы это был обычный класс:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes={
    MyClass1.class,
    Myclass2.class,
    MyJaxRsResource.class,
    MockServletContextAwareProcessor.class,
    MyCTest.Config.class
})
public class MyCTest
{
    @Configuration
    static class Config 
    {
          // Set up and register mocks here, and watch them be autowired!
          @Bean public DBService dbJobService() throws DBException
            {
                return mock(DBService.class); 
            }
    }

    @Autowired MyJaxRsResource myResource;

    @Test public void test() {
         String response = myResource.get("hello");
    }
}

@WebAppConfiguration внедряет свой собственный ServletContextAwareProcessor. Тем не менее, MockServletContextAwareProcessor необходимо, когда путь к распакованному WAR файлу должен быть установлен динамически, так как WebAppConfiguration позволяет только установить путь статически во время компиляции. Используя этот класс при запуске the-tests-in-the-server (см. Ниже), я ввожу настоящий ServletContext. Я использовал функцию профилей Spring для подавления ее через переменную среды (которая не очень элегантна). setServletContext вызывается просто сервером-испытателем.

@Configuration
public class MockServletContextAwareProcessor {

public static void setServletContext(ServletContext sc) {
    servletContext = sc;
}    
private static ServletContext getServletContext() {
    return servletContext;
}
private static ServletContext servletContext;    

@Configuration
@Profile("server-test")
static class ServerTestContext {

    static public @Bean
    ServletContextAwareProcessor 
        scap() {
            ServletContext sc = getServletContext();
            return new ServletContextAwareProcessor(sc);
    }
}    
}

Сервер-в-тестах с использованием Maven:

Шаг 1) Создайте регулярные тесты JUnit в папке /src/test, но назовите их IT *.java или * IT.java или * ITCase.java(например, MyClassIT.java). Вы можете назвать их по-разному, но это это то, что Failsafe ожидает по умолчанию. IT означает интеграционный тест, но тестовый код может находиться где угодно в тестовом континууме. Например, вы можете создать экземпляр класса и unit test, или вы можете запустить HttpClient (или Джерси-клиент), указать его на себя (обратите внимание на порт ниже) и функционально проверить свои точки входа.

public class CrossdomainPolicyResourceSTest extends BaseTestClass {

static com.sun.jersey.api.client.Client client;

  @BeforeClass public static void 
startClient() {

        client = Client.create();
    }

  @Test public void 
getPolicy() {

        String response = 
            client
                .resource("http://localhost/crossdomain.xml")
                .get(String.class);

        assertTrue(response.startsWith("<?xml version=\"1.0\"?>"));
    }
}

BaseTestClass - это всего лишь небольшой вспомогательный класс, который печатает имя тестового класса и тестирует его, как он выполняется (полезно для тестов на сервере, см. ниже):

public abstract class BaseTestClass {

@ClassRule public static TestClassName className = new TestClassName();
@Rule public TestName testName = new TestName();    

  @BeforeClass public static void 
printClassName() { 
        System.out.println("--" + className.getClassName() + "--"); 
    }    
  @Before public void 
printMethodName() {
        System.out.print(" " + testName.getMethodName()); 
    }    
  @After public void 
printNewLine() { 
        System.out.println(); 
    }
}

Шаг 2) Добавьте maven-failafe-plugin и maven-jetty-plugin в свой pom.xml

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>2.11</version>
    <executions>
        <execution>
            <goals>
                <goal>integration-test</goal>
                <goal>verify</goal>
            </goals>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>maven-jetty-plugin</artifactId>
    <version>6.1.26</version>
    <configuration>
        <!-- By default the artifactId is taken, override it with something simple -->
        <contextPath>/</contextPath>
        <scanIntervalSeconds>2</scanIntervalSeconds>
        <stopKey>foo</stopKey>
        <stopPort>9999</stopPort>
        <connectors>
            <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
                <port>9095</port>
                <maxIdleTime>60000</maxIdleTime>
            </connector>
        </connectors>
    </configuration>
    <executions>
        <execution>
            <id>start-jetty</id>
            <phase>pre-integration-test</phase>
            <goals>
                <goal>run</goal>
            </goals>
            <configuration>
                <scanIntervalSeconds>0</scanIntervalSeconds>
                <daemon>true</daemon>
            </configuration>
        </execution>
        <execution>
            <id>stop-jetty</id>
            <phase>post-integration-test</phase>
            <goals>
                <goal>stop</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Шаг 3) Прибыль. Действительно, это! Просто запустите "mvn install" или удалите сборку в среде IDE, и код будет создан, ваши обычные тесты Test.java будут запущены, сервер причалов будет запущен, будут выполняться тесты * IT.java, и вы будете получите хороший отчет.

Упаковка ваших тестов в WAR для работы в любом месте:

(используйте вместе или отдельно от вышеуказанных инструкций)

Шаг 1) Получите ваши тестовые классы (каталог src/test/), встроенные в WAR, указав плагин maven-war-plugin: (адаптирован из здесь)

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.1.1</version>
    <configuration>
        <failOnMissingWebXml>false</failOnMissingWebXml>
        <webResources>
            <resource>
                <directory>${project.build.directory}/test-classes</directory>
                <targetPath>WEB-INF/classes</targetPath>
            </resource>
            <resource>
                <directory>${project.build.directory}/test-libs</directory>
                <targetPath>WEB-INF/lib</targetPath>
            </resource>
        </webResources>
    </configuration>
</plugin>

Примечание. Вы можете создать отдельную WAR с интегрированными тестами, создав дополнительное выполнение и в своем наборе конфигурации и (подробности я оставляю читателю)

Примечание. В идеале вышеизложенное исключает все регулярные тесты (и только копирует * IT.java). Однако я не мог получить включенные/исключаемые для работы.

Вам также нужно будет включить тестовые библиотеки, добавив дополнительный модуль maven-dependency-plugin с целью зависимости от копирования, которая включает область проверки

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>2.1</version>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <excludeScope>compile</excludeScope>
                <outputDirectory>${project.build.directory}/test-libs</outputDirectory>
                <overWriteReleases>true</overWriteReleases>
                <overWriteSnapshots>true</overWriteSnapshots>
                <overWriteIfNewer>true</overWriteIfNewer>
            </configuration>
        </execution>
    </executions>
</plugin>

Если у maven-dependency-plugin уже есть другие исполнения (например, Netbeans вставляет один для javaee-endorsed-api), не удаляйте их.

Шаг 2) Программно запускайте свои тесты с помощью JUnitCore (JUnit4).

String runTests() {
    PrintStream sysOut = System.out;
    PrintStream sysErr = System.err;
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    PrintStream out = new PrintStream(stream);
    try {
        System.setOut(out);
        System.setErr(out);
        TextListener listener = new TextListener(out);
        JUnitCore junit = new JUnitCore();
        junit.addListener(listener);

        junit.run(MyClassIT.class,
                  AnotherClassIT.class,
                  ...etc...);

    } finally {
        System.setOut(sysOut);
        System.setErr(sysErr);
        out.close();
    }

    return stream.toString();
}

Шаг 3) Выполните тесты через JAX-RS

@Path("/test")
public class TestResource {

    @GET
    @Produces("text/plain")
    public String getTestResults() {

        return runTests();
    }

    private String runTests() {
        ...
    }

}

Поместите этот класс вместе с другими тестовыми классами (в src/test), чтобы он мог ссылаться на них.

Однако, если вы подклассифицируете класс javax.ws.rs.core.Application, в котором вы регистрируете все свои ресурсы, у вас возникнет проблема с ссылкой на TestResource (поскольку исходный код не может ссылаться на тестовый код). Чтобы обойти это, создайте полностью пустой фиктивный класс TestResource в src/main/... [тот же пакет]... Этот трюк работает, потому что фиктивный TestResource будет перезаписан реальным во время упаковки.

public class ShoppingApplication extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        return new HashSet<Class<?>>() {{
            add(TestResource.class);
        }};
    }

    @Override
    public Set<Object> getSingletons() {
        return new HashSet<Object>();
    }
}

package ...same package as the real TestResource...
public class TestResource {

}

Шаг 4) Настройте свою среду IDE для запуска/развертывания вашего приложения и автоматически откройте свой браузер, чтобы автоматически "/test" после сборки.

Ответ 2

Победившее ключевое слово оказывается "тестированием в контейнере". Совершенно новая и выдающаяся структура Arquillian.

Странно, похоже, ничего другого нет. Кто-то еще в StackOverflow спросил: "Я не вижу, чтобы какой-либо из этих проектов слишком широко использовался, так что что-то не так с тестированием внутри контейнера?" Но не получил ясного ответа.

Я предполагаю, что это лишь небольшая область между двумя большими сферами модульного тестирования и полным тестированием интеграции, которое должно быть охвачено тестированием внутри контейнера. Для меня тоже требуется только несколько тестов для проверки доступности и функциональности ресурсов сервера. Вероятно, следовало написать их вручную, чем потратить все это время на исследование (а затем на обучение) в контейнере.

Ответ 3

Используя Maven, Surefire может предоставить вам отформатированные отчеты о результатах тестирования.

http://maven.apache.org/plugins/maven-surefire-report-plugin/report-mojo.html

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

Ответ 4

Jakarta Cactus, похоже, сделал то, что я ищу. На этой домашней странице говорится: "Кактус - простая тестовая среда для модульного тестирования серверного Java-кода... Он использует JUnit... Кактус реализует стратегию в контейнере..." URL-адрес, такой как http://localhost:8080/test/ServletTestRunner?suite=TestSampleServlet предоставит довольно HTML-вывод.

Тем не менее, Apache Foundation поставил его в Аттике из-за отсутствия активного развития. Означает ли это, что я не должен думать об использовании этого? На странице Attic говорится, что "пользователям Cactus предлагается переключиться на другие методы тестирования", не объясняя, что это такое!

Ответ 5

Я не думаю, что существует стандартный способ, но вы можете исследовать с помощью Spring Remoting, чтобы вызвать методы на интересующем вас сервере, с вашего компьютера-разработчика. Если вы используете интерфейсы и вводите тестируемую службу, вы должны иметь возможность запускать один и тот же unit test дважды, один раз локально и один раз на сервере, просто изменив конфигурацию Spring.