Вопрос проверяет понимание паттерна Repository и его роли в тестировании, чтобы оценить умение проектировать тестируемую архитектуру приложения.
Паттерн Repository (Репозиторий) — это архитектурный подход, который инкапсулирует всю логику доступа к данным в отдельном слое. Он предоставляет абстрактный интерфейс для операций с данными (например, сохранение, получение, удаление), скрывая конкретные детали реализации, такие как SQL-запросы или вызовы API. Основная цель — отделить бизнес-логику приложения от механизмов хранения данных.
Ключевое преимущество — повышение тестируемости. Без репозитория код бизнес-логики часто напрямую вызывает методы доступа к данным (например, ORM-запросы), что делает его тестирование сложным и медленным, так как требует работы с реальной базой данных. Репозиторий решает эту проблему:
Рассмотрим простой сервис для управления пользователями. Сначала определим интерфейс репозитория, а затем его реализацию для работы с БД и для тестов.
// Интерфейс репозитория (абстракция)
interface IUserRepository {
findById(id: number): Promise;
save(user: User): Promise;
}
// Реализация для работы с реальной БД (например, через TypeORM)
class DatabaseUserRepository implements IUserRepository {
async findById(id: number): Promise {
// Реальный запрос к базе данных
return await UserEntity.findOne({ where: { id } });
}
async save(user: User): Promise {
await UserEntity.save(user);
}
}
// Сервис бизнес-логики, зависящий от абстракции
class UserService {
constructor(private userRepository: IUserRepository) {}
async activateUser(userId: number) {
const user = await this.userRepository.findById(userId);
if (!user) throw new Error('User not found');
user.isActive = true;
await this.userRepository.save(user);
}
}
Теперь напишем юнит-тест для UserService, используя мок репозитория:
// Тест с использованием мок-объекта (например, с Jest)
test('activateUser should activate found user', async () => {
// 1. Создаем мок репозитория
const mockUserRepository: IUserRepository = {
findById: jest.fn().mockResolvedValue({ id: 1, isActive: false }),
save: jest.fn().mockResolvedValue(undefined)
};
// 2. Создаем сервис, передавая мок
const userService = new UserService(mockUserRepository);
// 3. Выполняем тестируемый метод
await userService.activateUser(1);
// 4. Проверяем, что методы репозитория были вызваны корректно
expect(mockUserRepository.findById).toHaveBeenCalledWith(1);
expect(mockUserRepository.save).toHaveBeenCalledWith(
expect.objectContaining({ isActive: true })
);
});
Паттерн Repository широко используется в приложениях, построенных по принципам чистой архитектуры (Clean Architecture), Domain-Driven Design (DDD) или гексагональной архитектуры. Он особенно полезен в backend-разработке на платформах вроде .NET (с Entity Framework), Java (Spring Data JPA), Node.js (с TypeORM/Prisma) и других.
Вывод: Репозиторий стоит применять, когда вам необходимо обеспечить высокую тестируемость бизнес-логики, изолировав её от слоя данных. Это делает код более гибким, поддерживаемым и позволяет писать быстрые, стабильные юнит-тесты.