Тестирование логирования
Подключение
Для тестов следует подключать специальный модуль — LocalLoggerModule.forTesting(). Он
сохраняет логи в памяти и предоставляет к ним доступ через TestingLogger.
Это позволяет:
- перехватывать все вызовы логгера внутри тестируемого кода;
- сохранять их в память вместо вывода в консоль;
- проверять, какие именно логи были собраны за время теста.
Пример подключения через NestJS TestingModule:
let logger: TestingLogger;
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [LocalLoggerModule.forTesting()],
}).compile();
logger = module.get(TestingLogger);
});
После такого подключения любые вызовы вида:
this.logger.log('Project created');
this.logger.error('Project update failed');
будут сохранены внутри TestingLogger, и вы сможете проверить их в тестах при помощи матчеров:
logger.withMessage('Project created').withLevel('info').toBeLoggedOnce();
logger.withMessage('Project update failed').withLevel('error').toBeLogged();
Цепочка фильтров
Методы фильтрации (withMessage, withLevel, withContext, withDetails) можно вызывать
цепочкой, для более точного сопоставления логов:
logger
.withMessage('Project created')
.withLevel('info')
.withContext('Projects')
.withDetails({ user: 5 })
.toBeLoggedOnce();
Метод withDetails поддерживает частичное сравнение объекта, а также может проверять поля level,
context и message:
logger
.withMessage('Project created')
.withDetails({
level: 'info',
context: 'Projects',
user: 5,
})
.toBeLoggedOnce();
Просмотр найденных записей
Если нужно отладить фильтр, и просмотреть, какие записи были найдены по текущему фильтру, можно
использовать метод showLogs():
logger.withLevel('error').showLogs().toBeLogged();
Методы проверки
| Метод | Описание |
|---|---|
toBeLogged() | Проверяет, что хотя бы одна запись соответствует фильтру. |
toBeNotLogged() | Проверяет, что таких записей нет. |
toBeLoggedOnce() | Проверяет, что запись встречается ровно один раз. |
toBeLoggedTimes(n) | Проверяет, что запись встречается n раз. |
Все методы выбрасывают ошибку, если условие не выполняется.
Важно: Методы проверки обязательно должны вызываться, иначе проверки так и не будет.
Группирование проверок
Если в тесте нужно проверить несколько событий с общим набором полей, можно использовать такой приём:
const rbacLogger = logger.withContext('RBAC');
rbacLogger.withMessage('Access granted').toBeLoggedTimes(2);
rbacLogger.withMessage('Access denied').toBeLoggedOnce();
Это удобно для группировки проверок по контексту или другим общим параметрам.
Примеры использования
Проверка бизнес-события
it('должен логировать создание проекта', () => {
service.createProject({ user: 7 });
logger
.withMessage('Project created')
.withLevel('info')
.withDetails({ user: 7 })
.toBeLoggedOnce();
});
Проверка ошибок
it('должен логировать ошибку при невалидном статусе', () => {
service.updateProjectStatus(42, 'invalid');
logger.withMessage('Project update failed').withLevel('warn').toBeLogged();
});
Проверка отсутствия логов
it('не должен логировать ничего при успешном выполнении без событий', () => {
service.doNothing();
logger.any().toBeNotLogged();
});
Очистка
beforeEach(() => {
logger.clear();
});
Используйте clear() для сброса состояния между тестами, если экземпляр TestingLogger
переиспользуется в нескольких сценариях.
Рекомендации
TestingLoggerпредназначен только для тестов — в рабочем коде используется обычныйLogger.- Проверяйте факт логирования, а не реализацию — тесты должны оставаться устойчивыми к внутренним изменениям.
- Используйте
toBeNotLogged()для сценариев, где событие не должно быть зафиксировано.