1. Введение
В этой статье мы представим HtmlUnit, инструмент, который позволяет нам, проще говоря, программно взаимодействовать с HTML-сайтом и тестировать его, используя JAVA API .
2. О HtmlUnit
HtmlUnit — это браузер без графического интерфейса пользователя, предназначенный для программного использования, а не непосредственно пользователем.
Браузер поддерживает JavaScript (через движок Mozilla Rhino ) и может использоваться даже для веб-сайтов со сложными функциями AJAX. Все это можно сделать, имитируя типичный браузер с графическим интерфейсом, такой как Chrome или Firefox.
Название HtmlUnit может навести вас на мысль, что это фреймворк для тестирования, но хотя его определенно можно использовать для тестирования, он может делать гораздо больше.
Он также был интегрирован в Spring 4 и может без проблем использоваться вместе с тестовой средой Spring MVC.
3. Загрузка и зависимость от Maven
HtmlUnit можно скачать с SourceForge или с официального сайта . Кроме того, вы можете включить его в свой инструмент сборки (например, Maven или Gradle), как показано здесь . Например, это зависимость Maven, которую вы сейчас можете включить в свой проект:
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.23</version>
</dependency>
Новейшую версию можно найти здесь .
4. Веб-тестирование
Есть много способов протестировать веб-приложение, большинство из которых мы в тот или иной момент рассмотрели здесь на сайте.
С помощью HtmlUnit вы можете напрямую анализировать HTML-код сайта, взаимодействовать с ним так же, как обычный пользователь из браузера, проверять синтаксис JavaScript и CSS, отправлять формы и анализировать ответы, чтобы увидеть содержимое его HTML-элементов. Все это с использованием чистого Java-кода.
Начнем с простого теста: создадим WebClient
и получим первую страницу навигации www.foreach.com
:
private WebClient webClient;
@Before
public void init() throws Exception {
webClient = new WebClient();
}
@After
public void close() throws Exception {
webClient.close();
}
@Test
public void givenAClient_whenEnteringForEach_thenPageTitleIsOk()
throws Exception {
HtmlPage page = webClient.getPage("/");
Assert.assertEquals(
"ForEach | Java, Spring and Web Development tutorials",
page.getTitleText());
}
Вы можете увидеть некоторые предупреждения или ошибки при запуске этого теста, если на нашем веб-сайте есть проблемы с JavaScript или CSS. Вы должны исправить их.
Иногда, если вы знаете, что делаете (например, если вы видите, что единственные ошибки, которые у вас есть, связаны с сторонними библиотеками JavaScript, которые вы не должны изменять), вы можете предотвратить сбой теста из-за этих ошибок, вызвав setThrowExceptionOnScriptError
с помощью ложь
:
@Test
public void givenAClient_whenEnteringForEach_thenPageTitleIsCorrect()
throws Exception {
webClient.getOptions().setThrowExceptionOnScriptError(false);
HtmlPage page = webClient.getPage("/");
Assert.assertEquals(
"ForEach | Java, Spring and Web Development tutorials",
page.getTitleText());
}
5. Парсинг веб-страниц
Вам не нужно использовать HtmlUnit только для своих собственных веб-сайтов. В конце концов, это браузер: вы можете использовать его для навигации по любой сети, которая вам нравится, отправлять и получать данные по мере необходимости.
Извлечение, синтаксический анализ, хранение и анализ данных с веб-сайтов — это процесс, известный как парсинг веб-страниц, и HtmlUnit может помочь вам с извлечением и анализом частей.
В предыдущем примере показано, как мы можем войти на любой веб-сайт и перемещаться по нему, получая всю необходимую информацию.
Например, давайте перейдем к полному архиву статей ForEach, перейдем к последней статье и получим ее заголовок (первый тег <h1>
). Для нашего теста этого будет достаточно; но если бы мы хотели сохранить больше информации, мы могли бы, например, также получить заголовки (все теги <h2>
), таким образом имея общее представление о том, о чем статья.
Легко получить элементы по их идентификатору, но обычно, если вам нужно найти элемент, удобнее использовать синтаксис XPath . HtmlUnit позволяет нам его использовать, поэтому мы его и сделаем.
@Test
public void givenForEachArchive_whenRetrievingArticle_thenHasH1()
throws Exception {
webClient.getOptions().setCssEnabled(false);
webClient.getOptions().setJavaScriptEnabled(false);
String url = "/full_archive";
HtmlPage page = webClient.getPage(url);
String xpath = "(//ul[@class='car-monthlisting']/li)[1]/a";
HtmlAnchor latestPostLink
= (HtmlAnchor) page.getByXPath(xpath).get(0);
HtmlPage postPage = latestPostLink.click();
List<HtmlHeading1> h1
= (List<HtmlHeading1>) postPage.getByXPath("//h1");
Assert.assertTrue(h1.size() > 0);
}
Сначала обратите внимание, как — в данном случае нас не интересуют ни CSS, ни JavaScript, а мы просто хотим проанализировать макет HTML, поэтому мы отключили CSS и JavaScript.
В реальном парсинге вы можете взять, например , заголовки h1
и h2
, и результат будет примерно таким:
Java Web Weekly, Issue 135
1. Spring and Java
2. Technical and Musings
3. Comics
4. Pick of the Week
Вы можете проверить, действительно ли полученная информация соответствует последней статье в ForEach:
6. Что насчет AJAX?
Функциональность AJAX может быть проблемой, поскольку HtmlUnit обычно извлекает страницу до завершения вызовов AJAX. Много раз вам нужно, чтобы они закончили правильно протестировать ваш сайт или получить нужные данные. Есть несколько способов борьбы с ними:
- Вы можете использовать
webClient.setAjaxController(new NicelyResynchronizingAjaxController())
. Это повторно синхронизирует вызовы, выполняемые из основного потока, и эти вызовы выполняются синхронно, чтобы гарантировать наличие стабильного состояния для тестирования. - При входе на страницу веб-приложения вы можете подождать несколько секунд, чтобы было достаточно времени для завершения вызовов AJAX. Для этого вы можете использовать
webClient.waitForBackgroundJavaScript(MILLIS)
илиwebClient.waitForBackgroundJavaScriptStartingBefore(MILLIS)
. Вы должны вызывать их после получения страницы, но до работы с ней. - Вы можете подождать, пока не будет выполнено некоторое ожидаемое условие, связанное с выполнением вызова AJAX. Например:
for (int i = 0; i < 20; i++) {
if (condition_to_happen_after_js_execution) {
break;
}
synchronized (page) {
page.wait(500);
}
}
- Вместо создания
нового WebClient()
, который по умолчанию использует наиболее поддерживаемый веб-браузер, попробуйте другие браузеры, поскольку они могут лучше работать с вашими вызовами JavaScript или AJAX. Например, это создаст веб-клиент, использующий браузер Chrome:
WebClient webClient = new WebClient(BrowserVersion.CHROME);
7. Пример с Spring
Если мы тестируем собственное приложение Spring, все становится немного проще — нам больше не нужен работающий сервер .
Давайте реализуем очень простой пример приложения: просто контроллер с методом, который получает текст, и одна HTML-страница с формой. Пользователь может ввести текст в форму, отправить форму, и текст будет отображаться под этой формой.
В этом случае мы будем использовать шаблон Thymeleaf для этой HTML-страницы (вы можете увидеть полный пример Thymeleaf здесь ):
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { TestConfig.class })
public class HtmlUnitAndSpringTest {
@Autowired
private WebApplicationContext wac;
private WebClient webClient;
@Before
public void setup() {
webClient = MockMvcWebClientBuilder
.webAppContextSetup(wac).build();
}
@Test
public void givenAMessage_whenSent_thenItShows() throws Exception {
String text = "Hello world!";
HtmlPage page;
String url = "http://localhost/message/showForm";
page = webClient.getPage(url);
HtmlTextInput messageText = page.getHtmlElementById("message");
messageText.setValueAttribute(text);
HtmlForm form = page.getForms().get(0);
HtmlSubmitInput submit = form.getOneHtmlElementByAttribute(
"input", "type", "submit");
HtmlPage newPage = submit.click();
String receivedText = newPage.getHtmlElementById("received")
.getTextContent();
Assert.assertEquals(receivedText, text);
}
}
Ключевым моментом здесь является создание объекта WebClient с помощью
MockMvcWebClientBuilder
из WebApplicationContext
. С помощью WebClient
мы можем получить первую страницу навигации (обратите внимание, как она обслуживается localhost
) и начать просмотр оттуда.
Как видите, тест анализирует форму, вводит сообщение (в поле с идентификатором «сообщение»), отправляет форму и на новой странице утверждает, что полученный текст (поле с идентификатором «получено») является такой же, как текст, который мы представили.
8. Заключение
HtmlUnit — отличный инструмент, который позволяет вам легко тестировать свои веб-приложения, заполняя поля форм и отправляя их так же, как если бы вы использовали Интернет в браузере.
Он легко интегрируется с Spring 4, и вместе с Spring MVC Test framework они предоставляют вам очень мощную среду для проведения интеграционных тестов всех ваших страниц даже без веб-сервера.
Кроме того, с помощью HtmlUnit вы можете автоматизировать любые задачи, связанные с просмотром веб-страниц, такие как выборка, синтаксический анализ, хранение и анализ данных (веб-скрапинг).
Вы можете получить код на Github .