Jasmine — это фреймворк для модульного тестирования JavaScript, который строится вокруг понятий спеков (specs) и хуков (hooks). Понимание scope (области видимости) и контекста в хуках является ключевым для написания надежных и предсказуемых тестов.
В Jasmine предусмотрены несколько видов хуков:
beforeAll(fn) — выполняется один раз
перед всеми тестами в текущем describe.beforeEach(fn) — выполняется перед
каждым тестом в блоке.afterEach(fn) — выполняется после
каждого теста.afterAll(fn) — выполняется один раз
после всех тестов в блоке.Эти хуки позволяют организовать подготовку окружения, очистку ресурсов и установку начальных условий для тестов.
Локальные переменные в хуках ограничены блоком
describe, в котором они объявлены. Рассмотрим пример:
describe('Пример scope', () => {
let counter = 0;
beforeEach(() => {
counter += 1;
});
it('первый тест', () => {
expect(counter).toBe(1);
});
it('второй тест', () => {
expect(counter).toBe(2);
});
});
Здесь переменная counter существует в области
видимости блока describe. Каждый вызов
beforeEach изменяет её значение, и это изменение видно во
всех тестах внутри этого блока.
Если переменная объявляется внутри
beforeEach, она будет локальной для этого вызова и
недоступной в других тестах:
beforeEach(() => {
let temp = 5; // доступна только в этом хуке
});
this в хукахJasmine предоставляет особый объект контекста для каждого
it, который можно использовать для передачи данных между
хуками и тестами:
describe('Контекст this', function() {
beforeEach(function() {
this.value = 10;
});
it('использует this', function() {
expect(this.value).toBe(10);
});
it('можно изменить this', function() {
this.value += 5;
expect(this.value).toBe(15);
});
});
Особенности:
this уникален для каждого теста.() => {})
разрушает доступ к this, так как стрелочные функции
наследуют контекст из внешнего блока.describeВложенные describe создают иерархию
scope и позволяют локализовать переменные и хуки:
describe('Внешний блок', () => {
let outer = 'наружу';
beforeEach(() => {
outer += '!';
});
describe('Внутренний блок', () => {
let inner = 'внутри';
beforeEach(() => {
inner += '?';
});
it('доступ к обеим переменным', () => {
expect(outer).toBe('наружу!');
expect(inner).toBe('внутри?');
});
});
});
Ключевые моменты:
beforeEach внешнего блока выполняется
до beforeEach внутреннего блока.beforeAll и beforeEachbeforeAll удобно использовать для операций, которые
не требуют сброса состояния перед каждым тестом,
например, загрузки данных из фикстур или инициализации сервера.
describe('beforeAll vs beforeEach', () => {
let db;
beforeAll(() => {
db = initializeDatabase();
});
beforeEach(() => {
db.reset();
});
it('первый тест', () => {
expect(db.isEmpty()).toBeTrue();
});
it('второй тест', () => {
expect(db.isEmpty()).toBeTrue();
});
});
Здесь база данных инициализируется один раз, а каждый тест начинает с
чистой базы, благодаря db.reset() в
beforeEach.
this. Это важно учитывать, если
планируется использовать this для хранения состояния между
хуками и тестами.beforeEach, потом внутренние. Для
afterEach порядок обратный: сначала внутренние, потом
внешние.describe, либо контекст
this.describe для
логического разделения функционала.beforeAll (инициализация)
и beforeEach (сброс состояния).Понимание scope и контекста в Jasmine позволяет создавать тесты, которые легко поддерживать, расширять и комбинировать, сохраняя ясность и предсказуемость поведения даже в сложных сценариях.