Testing
Problem
You want to assert in unit tests that specific messages were logged at the right level — without writing to a real console, file, or external service.
Solution
Use a test transport to capture log entries directly. This gives full access to the LogEntry structure (level, message, context, bindings) without brittle console output assertions.
ts
import { createLogger } from '@vielzeug/rune';
import type { LogEntry, Transport } from '@vielzeug/rune';
import { expect, it } from 'vitest';
function createTestTransport() {
const entries: LogEntry[] = [];
const transport: Transport = (entry) => entries.push(entry);
return { entries, transport };
}
it('emits errors when enabled', () => {
const { entries, transport } = createTestTransport();
const log = createLogger({ logLevel: 'error', transports: [transport] });
log.error('failure');
expect(entries).toHaveLength(1);
expect(entries[0].level).toBe('error');
expect(entries[0].message).toBe('failure');
});
it('suppresses debug when logLevel is warn', () => {
const { entries, transport } = createTestTransport();
const log = createLogger({ logLevel: 'warn', transports: [transport] });
log.debug('silent');
log.warn('loud');
expect(entries).toHaveLength(1);
});
it('includes pinned bindings in every entry', () => {
const { entries, transport } = createTestTransport();
const log = createLogger({ transports: [transport] }).withBindings({ requestId: 'abc' });
log.info('ok');
expect(entries[0].bindings).toMatchObject({ requestId: 'abc' });
});When testing consoleTransport output directly, spy on the relevant console method:
ts
import { afterEach, expect, it, vi } from 'vitest';
import { consoleTransport, createLogger } from '@vielzeug/rune';
afterEach(() => vi.restoreAllMocks());
it('writes error to console.error', () => {
const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
const log = createLogger({ transports: [consoleTransport({ timestamp: false })] });
log.error('boom');
expect(spy).toHaveBeenCalled();
});Pitfalls
- Asserting only on the final console payload can be brittle. Prefer checking the message and structured context arguments rather than the exact badge formatting.
- Remote handlers run asynchronously. If a test covers remote forwarding, await the microtask that delivers the handler call before asserting.
- Log level filtering applies in tests. If the test logger is created with
logLevel: 'error',logger.debug()calls produce no output — ensure the test logger level matches the calls under test.