Object Mother — это паттерн проектирования, который используется в тестировании для создания объектов с заранее определёнными значениями. Этот паттерн помогает сделать тесты более читаемыми и удобными, исключая необходимость повторного создания объектов с одинаковыми характеристиками. В контексте тестирования с Jasmine, Object Mother помогает создавать и инициализировать объекты для тестов с минимальными усилиями, а также способствует лучшему поддержанию тестов в будущем.
В процессе написания юнит-тестов часто возникает необходимость создания объектов с одними и теми же значениями для разных тестов. Эти объекты могут быть довольно сложными и требовать многократного повторения инициализации. В таких случаях создание специального класса или функции — “матери” объектов — позволяет вынести логику создания объектов в одно место. Это улучшает читаемость и облегчает поддержание тестов.
Преимущества использования Object Mother:
Для реализации Object Mother паттерна в Jasmine, необходимо создать утилитный объект или класс, который будет генерировать заранее настроенные объекты для использования в тестах.
Пример реализации:
// Файл: userMother.js
class UserMother {
static createDefaultUser() {
return {
name: 'John Doe',
email: 'john.doe@example.com',
age: 30
};
}
static createUserWithName(name) {
return {
name: name,
email: 'default@example.com',
age: 25
};
}
static createUserWithAge(age) {
return {
name: 'Default User',
email: 'default@example.com',
age: age
};
}
}
export default UserMother;
В этом примере класс UserMother предоставляет несколько
статических методов, которые создают пользователей с различными
аттрибутами. Метод createDefaultUser создаёт пользователя с
предустановленными значениями, тогда как другие методы позволяют
изменять некоторые параметры объекта, такие как имя или возраст.
В тестах такие объекты будут использоваться для обеспечения чистоты и наглядности тестов:
import UserMother from './userMother';
describe('User validation tests', () => {
it('should validate default user', () => {
const user = UserMother.createDefaultUser();
expect(user.name).toBe('John Doe');
expect(user.email).toBe('john.doe@example.com');
expect(user.age).toBe(30);
});
it('should validate user with custom name', () => {
const user = UserMother.createUserWithName('Jane Doe');
expect(user.name).toBe('Jane Doe');
expect(user.email).toBe('default@example.com');
expect(user.age).toBe(25);
});
it('should validate user with custom age', () => {
const user = UserMother.createUserWithAge(40);
expect(user.name).toBe('Default User');
expect(user.email).toBe('default@example.com');
expect(user.age).toBe(40);
});
});
Каждый из этих тестов использует метод из UserMother,
что делает код более читаемым и удобным для поддержания.
Хотя Object Mother паттерн помогает в большинстве случаев, есть ситуации, когда его применение может быть нецелесообразным. В частности, если структура объектов меняется очень часто или если объекты являются простыми (например, состоящими из одной-двух переменных), использование такого паттерна может избыточным. В таких случаях достаточно использовать базовую инициализацию объектов непосредственно в тестах.
В некоторых случаях создание объектов с различными параметрами может стать более сложным, если объекты содержат вложенные структуры или требуют дополнительных зависимостей. В таких случаях можно использовать методы, которые генерируют не только базовые объекты, но и связанные с ними сущности, например, связанные сущности или фейковые зависимости.
Пример:
// Файл: orderMother.js
class OrderMother {
static createOrderWithUser(user) {
return {
orderId: 'ORD123',
user: user,
items: [{ name: 'Laptop', price: 1200 }],
status: 'pending'
};
}
static createOrderWithStatus(status) {
return {
orderId: 'ORD123',
user: { name: 'John Doe', email: 'john.doe@example.com' },
items: [{ name: 'Laptop', price: 1200 }],
status: status
};
}
}
export default OrderMother;
В этом примере метод createOrderWithUser принимает
объект пользователя, который может быть создан через
UserMother, и генерирует заказ с этим пользователем. Метод
createOrderWithStatus позволяет указать статус заказа.
Использование таких методов в тестах делает их более гибкими:
import OrderMother from './orderMother';
import UserMother from './userMother';
describe('Order tests', () => {
it('should create order with user', () => {
const user = UserMother.createDefaultUser();
const order = OrderMother.createOrderWithUser(user);
expect(order.user.name).toBe('John Doe');
expect(order.items[0].name).toBe('Laptop');
expect(order.status).toBe('pending');
});
it('should create order with custom status', () => {
const order = OrderMother.createOrderWithStatus('shipped');
expect(order.status).toBe('shipped');
});
});
В этом примере создаются заказы с различными пользователями и статусами, что позволяет протестировать логику работы с этими данными.
Паттерн Object Mother предоставляет удобный способ создания объектов для тестирования в Jasmine. Он способствует улучшению читаемости тестов, снижает дублирование кода и упрощает поддержку тестов в долгосрочной перспективе. Правильное использование этого паттерна помогает сделать тесты более структурированными и предсказуемыми, что особенно важно при сложных тестах с множеством зависимостей и параметров.