🧬 Итерация 6: @Setup, @Teardown, @Context, @Inject — жизненный цикл и shared state
🎯 Цель
Реализовать жизненный цикл для тестов и возможность делиться состоянием между методами и шагами — декларативно и безопасно.
🧠 Проблема
Когда тесты становятся сложнее, появляется необходимость:
- подготавливать данные (логин, токен, пользователи)
- очищать ресурсы (удаление, завершение сессии)
- делиться переменными между методами
- логировать внутренние действия
В Vitest это решается beforeEach / afterEach и глобальными переменными —
но это не читаемо, не типизировано и не структурировано.
✅ Что добавили
@Setup()— вызывается перед каждым тестом@Teardown()— вызывается после каждого теста@Context()— создаёт shared-объект для передачи между методами@Inject()— декларативно подставляет значения в поля из контекста- Расширили
runTest()для автоматической поддержки всего этого
📦 Пример
@Feature('Сессия')
@Tag('session')
class SessionTests {
@Context()
ctx!: { token?: string; log?: string[] }
@Inject()
token!: string
@Setup()
async init() {
this.ctx.token = 'admin-token'
this.ctx.log = ['Токен создан']
}
@Teardown()
async cleanup() {
this.ctx.log?.push('Очистка контекста')
attach('Лог выполнения', this.ctx.log?.join('\n') ?? '', 'text/plain')
}
@TestCase('Получение токена')
@Description('Проверка, что токен создаётся и доступен через контекст и инжекцию')
async checkToken() {
await step('Проверка токена из @Context', () => {
expect(this.ctx.token).toBe('admin-token')
})
await step('Проверка токена из @Inject', () => {
this.token = this.ctx.token!
expect(this.token).toBe('admin-token')
})
}
}
🔍 Что это даёт
- Изоляцию логики подготовки/очистки
- Стандартизованный shared state
- Возможность вешать логику на
@Teardown— даже вложения в отчёт - Ясную и декларативную структуру сценариев
🤔 А нельзя ли просто?
Можно. Вот так:
let token: string
beforeEach(() => {
token = 'admin-token'
})
test('тест токена', () => {
expect(token).toBe('admin-token')
})
Работает. Просто. Без магии.
Но когда тестов становится 30+, и каждый — это бизнес-сценарий с шагами, контекстом и вложениями, жизненный цикл превращается в архитектурный элемент — а не в хаотичный beforeEach().
🧩 Наш подход
@Setup()=beforeEach()с контекстом@Teardown()=afterEach()+ логика@Context()= структурный shared state@Inject()= автоматическое внедрение переменных
Вместо “вызови руками” — “объяви намерение”
🧠 Killer-фичи DSL подхода (vs обычный vitest)
- Allure-Ready из коробки (
feature,severity,owner, шаги, лейблы) - Автоматический жизненный цикл —
setup,teardown, логирование - Модульная архитектура с иерархией
Suite → SubSuite - Параметризация сценариев (
@TestCase.each) - Тест = Документация (
@Description,@Feature) - Расширяемость — auto-labeling, ген генерации,
Context, future hooks
🚀 Что дальше?
@BeforeAll,@AfterAll— выполнение один раз на класс@Step— шаги как методы