Pagination, Filtering, Sorting

Approved

Какие ошибки чаще всего скрываются в списочных API и как проверять пагинацию, фильтры и сортировку без поверхностного покрытия.

Содержание

Списочные API кажутся простыми, но именно в них живёт много неприятных дефектов. Пока команда смотрит только на "список открывается", в фоне могут прятаться дубли, пропуски, нестабильный порядок, неверные total count, сломанные фильтры и несогласованность между страницами.

Для QA это важная тема, потому что списки - это почти везде: пользователи, заказы, платежи, логи, события, товары, задачи, комментарии. Если pagination, filtering и sorting реализованы плохо, продукт начинает вести себя "странно", а пользователи описывают это как хаос: записи пропадают, появляются дважды, фильтр работает не так, сортировка скачет, а на второй странице уже другие данные.

Зачем это нужно API

Когда данных мало, сервер может вернуть всё сразу. Но как только объём становится заметным, это перестаёт быть хорошей идеей: ответы становятся тяжёлыми, клиенту неудобно их рендерить, а backend тратит лишние ресурсы.

  • pagination - возвращает данные частями
  • filtering - отдаёт только нужный поднабор
  • sorting - определяет порядок элементов

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

Pagination

Pagination - это разбиение большого набора данных на страницы или порции.

  • offset-based pagination: page=2, limit=20 или offset=20&limit=20
  • cursor-based pagination: cursor=..., starting_after=..., next_page_token=...

Для QA важно понимать разницу.

Offset pagination проще для чтения и часто встречается в админках и обычных CRUD API. Но у неё есть слабое место: если данные между запросами меняются, состав страниц может "плыть". На первой странице пользователь увидел 20 записей, потом кто-то добавил новую запись, и на второй странице уже появились дубли или пропуски.

Cursor pagination обычно устойчивее на живых данных. Она лучше подходит для больших, часто меняющихся списков, событийных лент, транзакций и high-load систем. Но её труднее тестировать глазами, потому что вместо понятного номера страницы приходится следить за курсором и порядком элементов.

Для QA отсюда главный вывод: pagination нужно проверять не только на статичном наборе, но и на изменяющихся данных.

Filtering

Фильтрация ограничивает набор данных по условиям.

  • статус = active
  • created_at >= дата
  • price between X and Y
  • owner_id = текущий пользователь
  • search по имени или email
  • несколько фильтров одновременно

На вид это очень удобная функция, но именно здесь часто ломается логика.

  • фильтр частично игнорируется
  • два фильтра вместе работают иначе, чем по отдельности
  • пустой фильтр и отсутствующий фильтр трактуются по-разному
  • чувствительность к регистру неожиданная
  • фильтрация по дате ломается на timezone
  • фильтр на backend работает не так, как UI ожидает
  • часть данных скрыта из-за прав, и пользователь думает, что фильтр "сломался"

Для QA важно понимать, что filtering - это не только проверка правильного SQL-условия или query param. Это проверка того, что система возвращает именно тот срез данных, который обещает контракт.

Sorting

Sorting определяет порядок элементов в выдаче. Чаще всего это сортировка по дате, имени, статусу, приоритету, цене или другому полю.

Самая частая ошибка здесь в том, что сортировку проверяют слишком поверхностно: "вроде похоже на правильный порядок". На практике сортировка должна быть:

  • корректной
  • устойчивой
  • предсказуемой
  • совместимой с pagination

Особенно важна стабильность порядка. Если сортировка идёт только по полю с повторяющимися значениями, например по created_at с одинаковым timestamp или по status, порядок между одинаковыми элементами может плавать. А если поверх этого ещё включена пагинация, пользователь начинает видеть дубли и пропуски между страницами.

Для QA это значит, что сортировка должна проверяться не только "по одному полю", но и на tie cases, когда значения совпадают.

Что важно QA

Есть несколько вещей, которые почти всегда стоит проверять.

Для pagination:

  • первая страница
  • последняя страница
  • пустая страница
  • page size = 1
  • page size на верхней границе
  • page size больше допустимого
  • переход между соседними страницами
  • отсутствие дублей и пропусков между страницами
  • корректность has_more, next_cursor, total_count, если они есть

Для filtering:

  • один фильтр
  • несколько фильтров вместе
  • граничные значения
  • пустое значение
  • отсутствие фильтра
  • невалидное значение фильтра
  • фильтр по полям с правами доступа
  • фильтр по дате и времени

Для sorting:

  • ascending и descending
  • сортировка по разным полям
  • одинаковые значения у нескольких записей
  • сортировка вместе с фильтрами
  • сортировка вместе с pagination
  • поведение при недопустимом поле сортировки

Где ломается чаще всего

  • элемент есть и на первой, и на второй странице
  • элемент пропадает между страницами
  • total_count не совпадает с реальным количеством
  • фильтр применяется к данным, но не к count
  • сортировка визуально меняется между одинаковыми запросами
  • новая запись внезапно "вклинивается" и ломает навигацию по offset pagination
  • backend молча игнорирует неизвестный фильтр или сортировку
  • UI думает, что сортировка одна, а API отдаёт другую
  • фильтр по дате даёт разные результаты из-за timezone
  • фильтр и права доступа вместе дают неожиданный результат

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

Практический пример

Представь API списка заказов:

  • GET /orders?status=paid&sort=created_at_desc&limit=20

Что здесь важно QA:

  • действительно ли пришли только paid заказы
  • действительно ли они отсортированы по дате в нужную сторону
  • если у двух заказов одинаковое created_at, порядок остаётся стабильным
  • на следующей странице нет дублей и пропусков
  • total_count, если он есть, соответствует отфильтрованному набору
  • если во время листания появился новый заказ, pagination ведёт себя предсказуемо
  • невалидный sort или status не ломает сервис и обрабатывается внятно

Один и тот же список может выглядеть "почти рабочим", но на практике быть ненадёжным именно из-за этих деталей.

Что должен вынести QA из этой темы

  • Pagination, filtering и sorting нужно тестировать вместе, а не по отдельности.
  • Списочные API часто ломаются не в happy path, а на границах и комбинациях условий.
  • Offset pagination проста, но чувствительна к изменениям данных между запросами.
  • Cursor pagination устойчивее на живых данных, но требует другой логики проверки.
  • Стабильная сортировка критична для корректной pagination.