🧬 Итерация 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
— шаги как методы