Юнит-тестирование

Юнит-тестирование поведения UI-компонентов на основе Slim Sel ect требует изоляции логики работы селекта от реального DOM-браузера и управления состоянием через программные интерфейсы библиотеки. Основная сложность заключается в том, что Slim Select активно взаимодействует с DOM, заменяет стандартный <select>, создает собственную разметку и обрабатывает события выбора, поиска и обновления данных.

При тестировании подобных компонентов ключевая задача — проверка не визуального результата, а состояния модели: выбранных значений, корректности инициализации, реакции на изменения данных и событий API.


Для тестирования Slim Select обычно используется jsdom, так как библиотека предполагает наличие DOM-структуры.

Базовая подготовка тестовой среды включает создание HTML-элемента:

document.body.innerHTML = `
  <select id="test-select">
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
  </select>
`;

Инициализация экземпляра:

import SlimSelect fr om 'slim-select';

const slim = new SlimSelect({
  select: '#test-select'
});

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


Проверка инициализации

Первый слой тестирования — корректное создание экземпляра и преобразование исходного <select>.

Типичные проверки:

  • наличие созданного экземпляра
  • скрытие исходного <select>
  • появление контейнера Slim Select
  • корректное копирование опций

Пример:

test('инициализация SlimSelect создает инстанс и оборачивает select', () => {
  const slim = new SlimSelect({ select: '#test-select' });

  expect(slim).toBeDefined();
  expect(document.querySelector('.ss-main')).not.toBeNull();
});

Дополнительно проверяется, что оригинальный <select> становится скрытым:

const select = document.querySelector('#test-select');
expect(select.style.display).toBe('none');

Проверка состояния выбора

Одним из центральных аспектов является синхронизация состояния между UI и внутренней моделью.

Slim Select предоставляет API для установки значений:

slim.setSelected('2');

Юнит-тест должен проверять:

  • обновление внутреннего состояния
  • отражение выбранного значения в DOM
  • корректность множественного выбора (если включен isMultiple)
test('выбор значения обновляет состояние', () => {
  const slim = new SlimSelect({ select: '#test-select' });

  slim.setSelected('2');

  const select = document.querySelector('#test-select');
  expect(select.value).toBe('2');
});

При множественном выборе:

slim.setSelected(['1', '3']);
expect(slim.getSelected()).toEqual(['1', '3']);

Тестирование API getSelected и setSelected

Методы getSelected и setSelected являются основой взаимодействия с компонентом.

Проверяется:

  • корректность возвращаемых значений
  • устойчивость к некорректным данным
  • синхронизация с DOM
test('getSelected возвращает текущее значение', () => {
  const slim = new SlimSelect({ select: '#test-select' });

  slim.setSelected('1');

  expect(slim.getSelected()).toBe('1');
});

Обработка несуществующих значений:

slim.setSelected('999');
expect(slim.getSelected()).toBe(null);

Проверка обновления данных через setData

Slim Select позволяет динамически обновлять список опций.

slim.setData([
  { text: 'Alpha', value: 'a' },
  { text: 'Beta', value: 'b' }
]);

Юнит-тесты для этого сценария проверяют:

  • полную замену списка
  • обновление DOM-элементов
  • синхронизацию выбранного значения
test('setData обновляет список опций', () => {
  const slim = new SlimSelect({ select: '#test-select' });

  slim.setData([
    { text: 'A', value: 'a' },
    { text: 'B', value: 'b' }
  ]);

  expect(document.querySelectorAll('.ss-option').length).toBe(2);
});

Тестирование поиска и фильтрации

Если включен режим поиска, Slim Select фильтрует элементы на лету.

Проверяется:

  • корректность фильтрации
  • реакция на ввод строки
  • обновление списка отображаемых опций
slim.open();
slim.search('Al');

Проверка результата:

test('поиск фильтрует элементы', () => {
  const slim = new SlimSelect({
    select: '#test-select',
    searchText: 'Search...'
  });

  slim.open();
  slim.search('Tw');

  const visibleOptions = document.querySelectorAll('.ss-option:not(.ss-hide)');
  expect(visibleOptions.length).toBe(1);
});

Проверка событий

Slim Select генерирует события при изменении состояния. Основные сценарии тестирования связаны с onChange.

const slim = new SlimSelect({
  select: '#test-select',
  events: {
    afterChange: (newVal) => {
      callback(newVal);
    }
  }
});

Тестирование событий требует использования мок-функций:

test('afterChange вызывается при изменении выбора', () => {
  const callback = jest.fn();

  const slim = new SlimSelect({
    select: '#test-select',
    events: {
      afterChange: callback
    }
  });

  slim.setSelected('2');

  expect(callback).toHaveBeenCalled();
});

Тестирование открытия и закрытия

Состояние раскрытия списка также подлежит проверке.

Методы:

  • open()
  • close()

Проверка DOM-состояния:

test('open добавляет активное состояние', () => {
  const slim = new SlimSelect({ select: '#test-select' });

  slim.open();

  expect(document.querySelector('.ss-content').classList.contains('ss-open')).toBe(true);
});

Закрытие:

slim.close();
expect(document.querySelector('.ss-content').classList.contains('ss-open')).toBe(false);

Проверка destroy и очистки состояния

Корректное удаление экземпляра критично для предотвращения утечек памяти.

Метод destroy должен:

  • восстанавливать исходный <select>
  • удалять созданные DOM-элементы
  • очищать обработчики событий
test('destroy восстанавливает исходный select', () => {
  const slim = new SlimSelect({ select: '#test-select' });

  slim.destroy();

  expect(document.querySelector('.ss-main')).toBeNull();
  expect(document.querySelector('#test-select').style.display).not.toBe('none');
});

Проверка устойчивости к динамическим изменениям DOM

Slim Select может использоваться в SPA, где элементы появляются и исчезают динамически.

Тестируются сценарии:

  • повторная инициализация на том же элементе
  • изменение options после инициализации
  • удаление узла из DOM
document.querySelector('#test-select').remove();
expect(() => slim.setSelected('1')).not.toThrow();

Моки и подмена таймеров

При тестировании поиска и debounce-логики часто используется управление таймерами:

jest.useFakeTimers();

slim.search('a');

jest.runAllTimers();

Это позволяет контролировать асинхронные обновления UI без реального ожидания.


Стратегия покрытия тестами

Полноценное тестирование Slim Select-оберток обычно разделяется на уровни:

  • инициализация и разрушение
  • работа с состоянием
  • API методов
  • DOM-синхронизация
  • события
  • динамическое обновление данных

Особое внимание уделяется стабильности повторных операций, так как библиотека часто используется в формах, где компоненты пересоздаются или обновляются без перезагрузки страницы.


Особенности тестирования в реальных проектах

В интеграции со сборщиками модулей и фреймворками важно учитывать:

  • повторную инициализацию при hot reload
  • совместимость с React/Vue-обертками
  • отсутствие конфликтов с виртуальным DOM
  • корректную очистку при размонтировании компонентов

Тесты в таких условиях часто комбинируют юнит-подход и легкую интеграционную проверку поведения DOM-структуры.


Ошибки, выявляемые через тестирование

Юнит-тестирование позволяет обнаружить типичные проблемы:

  • рассинхронизация select и UI
  • утечки обработчиков событий
  • некорректное обновление options
  • потеря выбранного значения при setData
  • неконсистентность состояния после destroy и повторной инициализации