Files
UniVerse/docs/backend-unit-tests.md
T
serega404 ef2fd39508
Backend CI / build-and-test (push) Successful in 48s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 5s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 24s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Failing after 14s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 2s
feat: добавил новые юнит-тесты для сервисов и маппинга
2026-05-28 19:51:34 +03:00

9.5 KiB
Raw Blame History

Backend unit-тесты

Назначение

Unit- и service-тесты backend проверяют бизнес-логику без запуска HTTP API:

  • доменные правила записи на лекции;
  • преобразование сущностей в DTO;
  • фильтрацию, пагинацию и связи курсов;
  • дерево тегов;
  • управление ролями и профилями пользователей.

Security-тесты авторизации находятся в том же тестовом проекте, но это отдельный интеграционный набор: они запускают API через WebApplicationFactory и проверяют HTTP-доступ к endpoint-ам.

Где лежат файлы

Тестовый проект: UniVerse.Api.Tests.csproj.

Тестовый стек

  • xUnit - test runner и assertions.
  • NSubstitute - mock-объекты для сервисных зависимостей.
  • Microsoft.EntityFrameworkCore.InMemory - изолированная InMemory БД для service-тестов.

Каждый service-тест создает отдельный AppDbContext с уникальным именем базы через Guid.NewGuid(), чтобы данные разных тестов не пересекались.

Что покрыто

EnrollmentSlotPolicy

Проверяется, что GetLimitForLevel выбирает последний подходящий threshold:

  • уровни ниже первого правила получают базовый лимит;
  • уровни между threshold используют предыдущий лимит;
  • уровни выше последнего threshold используют максимальный лимит;
  • публичный список Rules остается в ожидаемом порядке.

MappingExtensions

Проверяется стабильность DTO-маппинга:

  • роли пользователя сортируются одинаково в UserDto, CurrentUserDto и UserAuthDto;
  • лекции корректно считают записи и используют fallback для отсутствующих navigation properties;
  • отзывы переносят поля LLM-анализа;
  • дерево тегов маппится рекурсивно.

CourseService

Проверяется поведение без HTTP-слоя:

  • совместная работа фильтров Search, IsSynced, TagId;
  • корректные TotalCount, Page, PageSize, TotalPages;
  • добавление связи курс-тег;
  • ошибки при повторной связи или отсутствующем курсе/теге.

TagService

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

  • фильтрация по TagType и ParentId;
  • сортировка по имени;
  • запрет создания дочернего тега без существующего родителя;
  • построение вложенного дерева тегов.

UserService

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

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

Security-тесты авторизации

Security-тесты находятся в EndpointAuthorizationTests.cs. Это интеграционные тесты, которые отправляют реальные HTTP-запросы в тестовый API через ApiWebApplicationFactory.

Они проверяют не бизнес-результат endpoint-а, а сам факт прохождения или блокировки авторизации:

  • анонимный запрос к защищенному endpoint-у получает 401 Unauthorized;
  • запрос с неподходящей ролью получает 403 Forbidden;
  • запрос с подходящей ролью не получает 401 или 403;
  • публичные endpoint-ы из AnonymousEndpoints доступны без JWT и не возвращают 401 от middleware авторизации.

Таблица защищенных endpoint-ов задается в методе AuthenticatedEndpoints. Каждый кейс описывает:

  • человекочитаемое имя сценария;
  • HTTP-метод;
  • URL;
  • роль, которая должна пройти авторизацию;
  • роли, которые должны получить 403;
  • опциональное JSON-тело запроса.

Для endpoint-ов, доступных любой авторизованной роли, используется обычная тестовая роль, чаще Student, и пустой список запрещенных ролей. Для endpoint-ов с несколькими разрешенными ролями добавляется отдельный кейс на каждую разрешенную роль, например Admin и Teacher.

JWT для ролей создаются через TestJwtFactory.BearerHeader(role). Это позволяет проверять backend-авторизацию без Microsoft OAuth flow и без реального входа пользователя.

Как обновлять security-тесты

При добавлении или изменении API endpoint-а нужно обновить EndpointAuthorizationTests:

  1. Если endpoint требует авторизации, добавьте его в AuthenticatedEndpoints.
  2. Укажите правильную роль или отдельные кейсы для нескольких ролей.
  3. Для role-specific endpoint-а заполните forbidden ролями, которые должны получать 403.
  4. Если endpoint публичный, добавьте его в AnonymousEndpoints.
  5. Для POST, PUT, PATCH endpoint-ов добавьте минимальное валидное тело запроса, чтобы тест дошел до авторизации и не падал на model binding раньше времени.

Security-тест считается успешным для правильной роли, если ответ не 401 и не 403. Это намеренно: после авторизации endpoint может вернуть 404, 400, 409 или другой доменный ответ из-за тестовых данных, и это не является ошибкой проверки доступа.

Как запускать

Из корня репозитория:

dotnet test backend/UniVerse.sln --no-restore

Как добавлять новые unit/service-тесты

  1. Размещайте тесты рядом с проверяемой областью внутри backend/UniVerse.Api.Tests.
  2. Для сервисов с EF используйте InMemory AppDbContext с уникальным именем базы.
  3. Мокайте только внешние зависимости и соседние сервисы через NSubstitute.
  4. Не запускайте WebApplicationFactory, если проверяется не HTTP/auth behavior.
  5. Покрывайте не только успешный сценарий, но и доменные ошибки: NotFoundException, ConflictException, ForbiddenException.

Текущий baseline

После добавления unit/service-тестов и с учетом существующих security-тестов полный backend test suite проходит:

Passed: 303, Failed: 0, Skipped: 0