Как разработчик Java EE, я склонен смешивать модульные тесты с интеграционными тестами, обычно используя Arquillian . Но иногда, по непонятным причинам, Arquillian не может выполнить эту работу (скажем, упаковка приложения для тестирования слишком непонятна). Поэтому я хочу развернуть свое приложение, а затем выполнить несколько тестов. Но я действительно хочу, чтобы мои тесты выполнялись тогда и только тогда, когда веб-приложение развернуто, если нет, то тесты пропускаются. Другими словами, я хочу, чтобы мой Jenkins автоматически потерпел неудачу, если нет JBoss . Для этого я могу использовать API управления JBoss HTTP и предположения JUnit .
Случай использования

Так что это код этого очень простого сервлета
@WebServlet(urlPatterns = "/myservlet")
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
response.getWriter().println("Hello from the REST endpoint");
} catch (Exception e) {
e.printStackTrace();
}
}
}
И это код, который использует клиентский API JAX-RS 2.0 для тестирования сервлета, просто пингуя URL (я мог бы сделать что-то более умное, но суть этого поста не в этом.
public class URLTest {
@Test
public void checksTheServletIsDeployed() throws Exception {
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost:8080/sampleJavaEEJBossUtil").path("myservlet");
assertEquals(Response.Status.OK.getStatusCode(), target.request(MediaType.TEXT_PLAIN).get().getStatus());
}
}
Я выполняю тест, пока приложение развернуто, и JBoss запущен, и тест зеленый. Как вы можете себе представить, если я выключу JBoss или откажусь от приложения, тест провалится. В этом случае, что я действительно хочу, это игнорировать тест.
JBoss Admin CLI
Допустим, веб-приложение развернуто, JBoss запущен, все в порядке … давайте проверим все это с помощью CLI JBoss . Вы найдете этот интерфейс командной строки в $ JBOSS_HOME / bin / jboss-cli.sh. Когда вы выполняете его, вы подключаетесь к JBoss и вводите несколько команд, чтобы получить статус сервера и других компонентов (например, источник данных, фабрики JMS …). Следующие команды проверяют, работает ли JBoss, а также развернут ли наш пример JavaEEJBossUtil.war:
$ ./jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect
[standalone@localhost:9990 /] :read-attribute(name=server-state)
{
"outcome" => "success",
"result" => "running"
}
[standalone@localhost:9990 /] /deployment=sampleJavaEEJBossUtil.war:read-attribute(name=status)
{
"outcome" => "success",
"result" => "OK"
}
JBoss Admin REST API
Помимо интерфейса командной строки, JBoss также имеет интерфейс REST для администрирования. Поэтому все команды, которые вы вводите в CLI, имеют HTTP-эквивалент. Чтобы узнать, запущен ли JBoss и запущен ли sampleJavaEEJBossUtil.war, мы можем просто зайти в браузер и ввести следующие URL:
http://localhost:9990/management?operation=attribute&name=server-state http://localhost:9990/management/deployment/sampleJavaEEJBossUtil.war?operation=attribute&name=status
Обратите внимание, что API администрирования JBoss находятся по адресу http: // localhost: 9990 / management . Тогда это просто вопрос прохождения определенных путей (например, развертывание) или параметров запроса (например, операция = атрибут)). Если вы введете эти URL-адреса в браузере, вы также заметите, что вам нужно войти в систему как администратор (сначала вам нужно создать пользователя / пароль с помощью утилиты $ JBOSS_HOME / bin / add-user.sh).
Для API администрирования JBoss требуется аутентификация DIGEST
Так что, если вы не хотите использовать браузер, а вместо этого использовать клиентский API JAX-RS 2.0 для подключения к нему? Давайте сначала используем curl, чтобы увидеть, что он нам говорит.
~$ curl http://localhost:9990/management -v * About to connect() to localhost port 9990 (#0) * Connected to localhost (127.0.0.1) port 9990 (#0) > GET /management HTTP/1.1 > User-Agent: curl/7.30.0 > Host: localhost:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",algorithm=MD5 < Content-Length: 77
Как видно из вывода в строке № 11, API администрирования JBoss требует аутентификации DIGEST. Итак, предположив, что пользователь и пароль — admin / admin, вам необходимо ввести следующее:
~$ curl --digest 'http://admin:admin@localhost:9990/management' -v * About to connect() to localhost port 9990 (#0) * Connected to localhost (127.0.0.1) port 9990 (#0) * Server auth using Digest with user 'admin' > GET /management HTTP/1.1 > User-Agent: curl/7.30.0 > Host: localhost:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive</pre> < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",algorithm=MD5 < Content-Length: 77 < * Ignoring the response-body * Issue another request to this URL: 'http://admin:admin@localhost:9990/management' * Connected to localhost (127.0.0.1) port 9990 (#0) * Server auth using Digest with user 'admin' > GET /management HTTP/1.1 > Authorization: Digest username="admin", realm="ManagementRealm", uri="/management", algorithm="MD5" > User-Agent: curl/7.30.0 > Host: localhost:9990 > Accept: */* > < HTTP/1.1 200 OK < Connection: keep-alive < Authentication-Info: nextnonce="mJnypxK1UwINMTQwNTgwNDA2MTMzMHie0+SV9V15urC9I8n075w=" < Content-Type: application/json; charset=utf-8 < Content-Length: 2189 < * Connection #0 to host localhost left intact
Как вы можете видеть, при использовании аутентификации DIGEST выходные данные являются гораздо более многословными, и в результате получается код возврата 200 (вместо 401). Итак, теперь у нас есть два важных компонента для доступа к API с помощью REST: мы знаем, как выглядят URL-адреса, и мы знаем, что нам нужно использовать аутентификацию DIGEST. Итак, давайте посмотрим, как это сделать.
Клиентский API JAX-RS для доступа к API управления JBoss
JAX-RS 2.0 имеет очень хороший клиентский API. Если мы хотим проверить, работает ли JBoss, нам нужно получить доступ к URL-адресу http: // localhost: 9990 / management? Operation = attribute & name = server-state и убедиться, что он возвращает «выполняется». С JAX-RS это так же просто, как написать:
public static boolean isJBossUpAndRunning() {
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost:9990/management").queryParam("operation", "attribute").queryParam("name", "server-state");
Response response = target.request(MediaType.APPLICATION_JSON).get();
return response.getStatus() == Response.Status.OK.getStatusCode() && response.readEntity(String.class).contains("running");
}
Но, к сожалению, это не сработает, потому что вернет 401 (Несанкционированный). Нам нужно аутентифицироваться с использованием схемы DIGEST. Как мы это делаем в JAX-RS 2.0? Ну, мы не можем: o (Единственный трюк, который я обнаружил (благодаря Биллу Бёрку и Аруну Гупте ), это использование HTTP-клиента Apache и расширение RESTEasy . Так что в приведенном ниже коде getClientmethod возвращает аутентифицированный HttpClient, который затем используется для доступа к API REST администрирования JBoss.
public class JBossUtil {
private static ResteasyClient getClient() {
// Setting digest credentials
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("admin", "admin");
credentialsProvider.setCredentials(AuthScope.ANY, credentials);
HttpClient httpclient = HttpClientBuilder.create().setDefaultCredentialsProvider(credentialsProvider).build();
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpclient, true);
// Creating HTTP client
return new ResteasyClientBuilder().httpEngine(engine).build();
}
public static boolean isJBossUpAndRunning() {
WebTarget target = getClient().target("http://localhost:9990/management").queryParam("operation", "attribute").queryParam("name", "server-state");
Response response = target.request(MediaType.APPLICATION_JSON).get();
return response.getStatus() == Response.Status.OK.getStatusCode() && response.readEntity(String.class).contains("running");
}
}
Здесь я упростил код (без обработки исключений) и показываю только метод isJBossUpAndRunning. Но вы можете проверить полный класс JBossUtil на GitHub , я создал другие методы.
Тестовый класс
Возвращаясь к нашему варианту использования, теперь у нас есть все части головоломки. Мы знаем, как проверить, что JBoss запущен и работает (мы могли бы даже убедиться, что веб-приложение действительно развернуто ), и мы можем использовать предположения JUnit . Следующий тест будет выполняться только тогда и только тогда, когда JBoss запущен и работает. Если нет, то тест будет автоматически игнорироваться благодаря API Assume .
public class URLTest {
@Test
public void checksTheServletIsDeployed() throws Exception {
Assume.assumeTrue(JBossUtil.isJBossUpAndRunning());
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost:8080/sampleJavaEEJBossUtil").path("myservlet");
assertEquals(Response.Status.OK.getStatusCode(), target.request(MediaType.TEXT_PLAIN).get().getStatus());
}
}
Вывод
Модульное тестирование важно, но когда вы выполняете свой код в контейнере, интеграционные тесты также очень полезны. Arquillian решает большинство проблем и упрощает интеграционное тестирование, но можно использовать и другую технику. Основная цель нашей системы сборки — пройти все тесты … или игнорировать некоторые. С учетом предположений JUnit и некоторых API REST наши тесты могут быть осведомлены и выполняться только в том случае, если контейнер запущен и работает, если развернуто веб-приложение или источник данных действительно включен. Загрузите код , попробуйте и дайте мне знать, что вы думаете.
И спасибо Биллу Бёрку , Аруну Гупте , Алексису Хасслеру и Христосу Василакису за помощь и советы.
