Интеграционное тестирование предназначено для проверки корректной работы библиотеки Awesomplete в составе более крупного приложения. Если модульное тестирование оценивает отдельные методы и функции изолированно, то интеграционные тесты исследуют взаимодействие между компонентами системы: пользовательским интерфейсом, сетевыми запросами, обработчиками событий, внешними библиотеками и серверной частью.
Для автодополнения особенно важны сценарии, в которых библиотека получает данные из внешних источников, взаимодействует с формами и реагирует на действия пользователя.
Основные задачи интеграционного тестирования:
Модульный тест может проверять работу фильтра списка:
expect(filter("Ja")).toEqual([
"Java",
"JavaScript"
]);
Такой тест не показывает, будет ли список действительно отображён в интерфейсе.
Интеграционный тест охватывает весь процесс:
Проверяется полный сценарий использования.
Самый распространённый вариант тестирования.
Проверяется:
Пример подготовки среды:
document.body.innerHTML = `
<input id="search">
`;
const input = document.querySelector("#search");
const awesomplete = new Awesomplete(input, {
list: ["Java", "JavaScript", "Python"]
});
После ввода значения проверяется состояние DOM:
input.value = "Ja";
input.dispatchEvent(new Event("input"));
expect(
document.querySelectorAll("li").length
).toBe(2);
Часто данные для автодополнения загружаются через API.
Схема работы:
Input
↓
fetch()
↓
JSON
↓
Awesomplete.list
↓
Dropdown
Тест проверяет всю цепочку.
Пример:
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve([
"Java",
"JavaScript"
])
})
);
Код приложения:
input.addEventListener("input", async () => {
const response = await fetch("/search");
awesomplete.list =
await response.json();
});
Интеграционный тест:
input.value = "Ja";
input.dispatchEvent(
new Event("input")
);
await Promise.resolve();
expect(fetch)
.toHaveBeenCalled();
Проверяется взаимодействие между пользовательским вводом, сетью и компонентом автодополнения.
Интеграционные тесты ориентированы на реальные действия пользователя.
Подготовка:
const awesomplete = new Awesomplete(input, {
list: ["Java", "JavaScript"]
});
Имитация ввода:
input.value = "Ja";
input.dispatchEvent(
new Event("input")
);
Выбор первого варианта:
const firstItem =
document.querySelector("li");
firstItem.click();
Проверка результата:
expect(input.value)
.toBe("Java");
Тест отражает типичный пользовательский процесс.
input.value = "";
input.dispatchEvent(
new Event("input")
);
Проверка отсутствия подсказок:
expect(
document.querySelector("ul")
.children.length
).toBe(0);
input.value = "J";
input.dispatchEvent(new Event("input"));
input.value = "Ja";
input.dispatchEvent(new Event("input"));
input.value = "Jav";
input.dispatchEvent(new Event("input"));
Проверяется корректное обновление списка на каждом этапе.
Во многих проектах список вариантов хранится на сервере.
Пример приложения:
async function loadSuggestions(query) {
const response =
await fetch(`/api?q=${query}`);
return response.json();
}
Интеграционный тест должен удостовериться, что:
Проверка:
expect(fetch)
.toHaveBeenCalledWith(
"/api?q=java"
);
Awesomplete часто используется совместно с асинхронными запросами.
Пример:
input.addEventListener(
"input",
async () => {
const result =
await fetchData();
awesomplete.list = result;
}
);
Тест:
await waitFor(() => {
expect(
awesomplete.list.length
).toBeGreaterThan(0);
});
Проверяется завершение всей асинхронной цепочки.
Библиотека Testing Library позволяет выполнять тестирование максимально близко к поведению пользователя.
Создание поля:
render(`
<input id="search">
`);
Поиск элемента:
const input =
screen.getByRole("textbox");
Ввод текста:
await userEvent.type(
input,
"Ja"
);
Проверка результатов:
expect(
screen.getByText("Java")
).toBeInTheDocument();
Такой подход уменьшает зависимость тестов от внутренней реализации компонента.
Awesomplete генерирует собственные события.
Наиболее важные:
Интеграционный тест позволяет убедиться, что события возникают в нужной последовательности.
Пример:
const handler = jest.fn();
input.addEventListener(
"awesomplete-select",
handler
);
Выбор элемента:
document
.querySelector("li")
.click();
Проверка:
expect(handler)
.toHaveBeenCalled();
Автодополнение редко используется отдельно от форм.
Типичный сценарий:
<form id="user-form">
<input id="city">
</form>
После выбора подсказки:
expect(city.value)
.toBe("London");
Дополнительно проверяется отправка формы:
form.dispatchEvent(
new Event("submit")
);
Если поле участвует в бизнес-логике приложения, интеграционный тест должен удостовериться в правильности всей цепочки обработки.
В React Awesomplete обычно создаётся внутри жизненного цикла компонента.
Пример:
useEffect(() => {
new Awesomplete(
inputRef.current,
{
list
}
);
}, [list]);
Интеграционный тест проверяет:
Пример:
render(<Search />);
expect(
document.querySelector("input")
).toBeTruthy();
После обновления состояния:
rerender(
<Search data={newData}/>
);
Проверяется появление новых вариантов.
Пример инициализации:
mounted() {
this.awesomplete =
new Awesomplete(
this.$refs.input
);
}
Тестирование:
const wrapper =
mount(Component);
Поиск поля:
const input =
wrapper.find("input");
Имитация ввода:
await input.setValue("Ja");
Проверка отображения подсказок:
expect(
document.querySelectorAll("li")
.length
).toBeGreaterThan(0);
Во многих старых проектах Awesomplete используется совместно с jQuery.
Пример:
$("#search").on(
"change",
updateData
);
Интеграционный тест проверяет, что:
Проверка:
const spy = jest.fn();
$("#search").on(
"change",
spy
);
После выбора элемента:
expect(spy)
.toHaveBeenCalled();
Интеграционные тесты должны охватывать аварийные сценарии.
Например, сервер недоступен:
global.fetch = jest.fn(() =>
Promise.reject(
new Error("Network Error")
)
);
Проверка:
await expect(
loadSuggestions("java")
)
.rejects.toThrow();
Другой вариант — получение пустого списка:
Promise.resolve([]);
Ожидаемое поведение:
expect(
document.querySelectorAll("li")
.length
).toBe(0);
При больших объёмах данных важно удостовериться, что интеграция не приводит к деградации интерфейса.
Создание большого массива:
const items =
Array.from(
{ length: 10000 },
(_, i) => `Item ${i}`
);
Инициализация:
const awesomplete =
new Awesomplete(input, {
list: items
});
Измерение времени:
const start =
performance.now();
input.value = "Item";
input.dispatchEvent(
new Event("input")
);
const end =
performance.now();
Проверка:
expect(end - start)
.toBeLessThan(100);
Подобные тесты позволяют обнаруживать проблемы взаимодействия между алгоритмами поиска и интерфейсом.
Крупные проекты обычно группируют тесты по пользовательским сценариям.
Пример структуры:
tests/
│
├── integration/
│ ├── search.test.js
│ ├── api.test.js
│ ├── form.test.js
│ ├── events.test.js
│ └── errors.test.js
Внутри сценариев выделяются отдельные группы проверок:
describe(
"Search Integration",
() => {
test(
"loads suggestions",
() => {}
);
test(
"selects item",
() => {}
);
test(
"updates form",
() => {}
);
}
);
Такая организация упрощает сопровождение тестового набора и обеспечивает контроль над всеми ключевыми точками взаимодействия библиотеки Awesomplete с остальной частью приложения.