При тестировании JavaScript-программ с использованием Jest часто возникает необходимость имитировать поведение классов или их методов. Мокирование классов позволяет изолировать тестируемый код от внешних зависимостей, делая тесты быстрее и стабильнее. В этой главе рассматриваются основные методы мокирования ES6 классов с использованием Jest.
Мокирование классов необходимо для тестирования компонентов или модулей, которые зависят от сложных или внешних объектов, например, API, баз данных, файловых систем или сторонних библиотек. В случае с ES6 классами, мокирование помогает:
Jest предоставляет несколько способов мокирования классов, включая
использование функций jest.mock() и jest.fn().
Для начала рассмотрим, как можно замокировать простой ES6 класс.
class MyClass {
method1() {
return "real value";
}
method2() {
return "real value 2";
}
}
// Замокировать класс MyClass
jest.mock('./MyClass', () => {
return jest.fn().mockImplementation(() => {
return {
method1: jest.fn().mockReturnValue('mocked value'),
method2: jest.fn().mockReturnValue('mocked value 2')
};
});
});
В этом примере класс MyClass мокируется с помощью
jest.mock(), а его методы заменяются на мок-версии с
заранее заданными значениями.
Иногда полезно замокировать только некоторые методы класса, оставив остальные в их оригинальном виде. Для этого можно использовать частичное мокирование.
jest.mock('./MyClass', () => {
const originalClass = jest.requireActual('./MyClass');
return {
...originalClass,
method1: jest.fn().mockReturnValue('mocked value')
};
});
В этом примере метод method1 заменяется на
замокированную версию, а метод method2 сохраняет свою
оригинальную реализацию.
Для мокирования конструктора класса, который создается с помощью
new, также можно использовать Jest. Важно понимать, что
jest.fn() и jest.mock() работают не только с
функциями, но и с конструкторами классов.
class MyClass {
constructor() {
this.value = 42;
}
method() {
return this.value;
}
}
jest.mock('./MyClass', () => {
return jest.fn().mockImplementation(() => {
return { method: jest.fn().mockReturnValue('mocked value') };
});
});
const instance = new MyClass();
console.log(instance.method()); // Выведет "mocked value"
Здесь MyClass замокирован так, чтобы при создании
экземпляра возвращался объект с замокированным методом
method.
Многие методы классов в реальных приложениях асинхронны. Для
мокирования асинхронных методов используется jest.fn() с
асинхронными колбэками.
class MyClass {
async fetchData() {
return 'real data';
}
}
jest.mock('./MyClass', () => {
return jest.fn().mockImplementation(() => {
return { fetchData: jest.fn().mockResolvedValue('mocked data') };
});
});
const instance = new MyClass();
instance.fetchData().then(result => console.log(result)); // "mocked data"
В данном примере метод fetchData замокирован с
использованием mockResolvedValue(), что позволяет вернуть
промис с заданным значением. Также можно использовать
mockRejectedValue() для эмуляции ошибок.
После мокирования классов и их методов часто требуется убедиться, что методы были вызваны, а также какие аргументы были переданы. Jest предоставляет удобные методы для таких проверок.
const mockMethod = jest.fn();
class MyClass {
method1(arg) {
mockMethod(arg);
}
}
const instance = new MyClass();
instance.method1('test');
expect(mockMethod).toHaveBeenCalledWith('test');
expect(mockMethod).toHaveBeenCalledTimes(1);
В этом примере проверяется, что метод method1 был вызван
с аргументом 'test' и вызван один раз.
Перезапуск моков: Jest автоматически сбрасывает
мокированные данные между тестами, но если необходимо сбросить
мокированные методы вручную, можно использовать
jest.clearAllMocks() или
jest.resetAllMocks().
Мокирование зависимостей классов: В случае, если
один класс зависит от другого, например, при создании экземпляра одного
класса в другом, можно замокировать зависимость через
jest.mock().
Мокирование статических методов: Статические
методы классов можно замокировать аналогично обычным методам, с
использованием jest.fn() и других вспомогательных
функций.
class MyClass {
static staticMethod() {
return 'real value';
}
}
jest.mock('./MyClass', () => {
return {
...jest.requireActual('./MyClass'),
staticMethod: jest.fn().mockReturnValue('mocked value')
};
});
Рассмотрим более сложный пример, где классы имеют зависимости от других классов и взаимодействуют с внешними ресурсами. В этом случае можно замокировать все внешние зависимости, чтобы тестировать только основную логику.
class Database {
save(data) {
// Реальная логика сохранения данных
return 'data saved';
}
}
class Service {
constructor(db) {
this.db = db;
}
async saveData(data) {
return await this.db.save(data);
}
}
jest.mock('./Database', () => {
return jest.fn().mockImplementation(() => {
return { save: jest.fn().mockResolvedValue('mocked data saved') };
});
});
const db = new Database();
const service = new Service(db);
service.saveData('test').then(result => console.log(result)); // "mocked data saved"
Здесь класс Database мокируется, чтобы метод
save возвращал замокированное значение. Это позволяет
протестировать класс Service, не затрагивая реальную логику
работы с базой данных.
Мокирование ES6 классов в Jest — мощный инструмент для создания изолированных и стабильных тестов. Оно позволяет легко контролировать поведение классов и их методов, имитировать асинхронные операции, а также проверять, как методы и конструкторы были вызваны в процессе тестирования.