Перейти к содержимому

7. Matchers и Assertions

Matchers

// ─── Примитивы ───────────────────────────────────────────
expect(42).toBe(42); // строгое равенство (===)
expect(42).not.toBe(43);
expect('hello').toBe('hello');
// ─── Объекты и массивы ───────────────────────────────────
expect({ a: 1, b: 2 }).toEqual({ a: 1, b: 2 }); // глубокое равенство
expect([1, 2, 3]).toEqual([1, 2, 3]);
// ─── Null / Undefined / Boolean ──────────────────────────
expect(null).toBeNull();
expect(undefined).toBeUndefined();
expect('hello').toBeDefined();
expect(true).toBeTruthy();
expect(0).toBeFalsy();
expect('').toBeFalsy();
expect([]).toBeTruthy(); // ВНИМАНИЕ: пустой массив — truthy!
// ─── Числа ───────────────────────────────────────────────
expect(10).toBeGreaterThan(5);
expect(10).toBeGreaterThanOrEqual(10);
expect(5).toBeLessThan(10);
expect(5).toBeLessThanOrEqual(5);
expect(0.1 + 0.2).toBeCloseTo(0.3, 5); // для float!
// ─── Строки ──────────────────────────────────────────────
expect('hello world').toContain('world');
expect('hello').toMatch(/^hel/);
expect('hello').toMatch('ell');
expect('hello world').toHaveLength(11);
// ─── Массивы ─────────────────────────────────────────────
expect([1, 2, 3]).toContain(2);
expect([1, 2, 3]).toHaveLength(3);
expect([1, 2, 3]).toContainEqual({ id: 1 }); // глубокое в массиве
// ─── Объекты ─────────────────────────────────────────────
expect({ name: 'Alice', age: 28 }).toMatchObject({ name: 'Alice' });
expect({ id: 1, name: 'Alice' }).toHaveProperty('name');
expect({ id: 1, name: 'Alice' }).toHaveProperty('name', 'Alice');
expect({ a: { b: 2 } }).toHaveProperty('a.b', 2);
// ─── Ошибки ──────────────────────────────────────────────
expect(() => JSON.parse('{invalid}')).toThrow();
expect(() => JSON.parse('{invalid}')).toThrow(SyntaxError);
expect(() => { throw new Error('Oops') }).toThrow('Oops');
expect(() => { throw new Error('Oops') }).toThrow(/oops/i);

Используются когда точное значение неизвестно заранее.

// expect.any() — любое значение типа
expect({ id: 42, name: 'Alice' }).toMatchObject({
id: expect.any(Number),
name: expect.any(String),
});
// expect.anything() — любое не-null значение
expect({ token: 'abc123', expires: new Date() }).toMatchObject({
token: expect.anything(),
expires: expect.anything(),
});
// expect.arrayContaining() — массив содержит элементы
expect([1, 2, 3, 4]).toEqual(expect.arrayContaining([2, 4]));
expect(['alice', 'bob', 'carol']).toEqual(
expect.arrayContaining(['alice', 'carol'])
);
// expect.objectContaining() — объект содержит поля
expect({
id: 1,
name: 'Alice',
createdAt: new Date(),
}).toMatchObject(
expect.objectContaining({
id: expect.any(Number),
name: 'Alice',
})
);
// expect.stringContaining() / stringMatching()
expect('Hello, World!').toEqual(expect.stringContaining('World'));
expect('[email protected]').toEqual(expect.stringMatching(/^[^\s@]+@/));
// Расширяем expect собственными проверками
expect.extend({
toBeValidEmail(received) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const pass = re.test(received);
return {
message: () =>
pass
? `Expected ${received} not to be a valid email`
: `Expected ${received} to be a valid email`,
pass,
};
},
toBeWithinRange(received, min, max) {
const pass = received >= min && received <= max;
return {
message: () =>
`Expected ${received} to ${pass ? 'not ' : ''}be within [${min}, ${max}]`,
pass,
};
},
});
// TypeScript (глобальная декларация)
declare global {
namespace jest {
interface Matchers<R> {
toBeValidEmail(): R;
toBeWithinRange(min: number, max: number): R;
}
}
}
// Использование
test('custom matchers', () => {
expect('[email protected]').toBeValidEmail();
expect('notanemail').not.toBeValidEmail();
expect(5).toBeWithinRange(1, 10);
});
// Стандартно: тест останавливается на первой ошибке
test('all fields valid', () => {
const user = createUser();
expect(user.id).toBeDefined(); // если падает здесь — остальное не проверяется
expect(user.name).toBe('Alice');
expect(user.email).toBeValidEmail();
});
// Soft assertions: проверяем все и сообщаем все ошибки
test('all fields valid (soft)', () => {
const user = createUser();
const errors = [];
try { expect(user.id).toBeDefined(); } catch(e) { errors.push(e.message); }
try { expect(user.name).toBe('Alice'); } catch(e) { errors.push(e.message); }
try { expect(user.email).toBeValidEmail(); } catch(e) { errors.push(e.message); }
if (errors.length) throw new Error(errors.join('\n'));
});
import { equals, subsetEquality } from '@jest/expect-utils';
// Глубокое равенство программно
const isEqual = equals({ a: 1 }, { a: 1 }); // true
  1. Создай custom matcher toBePositive() и toBeNegative()
  2. Используй asymmetric matchers для тестирования API ответа с динамическим ID
  3. Напиши тест с test.each для проверки набора email адресов
  • toBe — строгое равенство, toEqual — глубокое
  • toMatchObject — частичное совпадение объекта
  • Asymmetric matchers — для динамических значений
  • Custom matchers — расширяем expect под свою бизнес-логику