Jest captures the stdout and stderr output. Is it possible to access this captured information in a test?
Regards, nidkil
Jest captures the stdout and stderr output. Is it possible to access this captured information in a test?
Regards, nidkil
I was going about this in the wrong way. Instead of using spy/mock I was trying to intercept stdout/stderr directly. I solved it using the following function.
/* eslint-disable no-undef */
export function spyConsole() {
let spy = {}
beforeEach(() => {
spy.console = jest.spyOn(console, 'error').mockImplementation(() => {})
})
afterEach(() => {
spy.console.mockClear()
})
afterAll(() => {
spy.console.mockRestore()
})
return spy
}
Which is used in the following way:
import { createLocalVue, mount } from '@vue/test-utils'
import { spyConsole } from '@tst/helpers/test-utils'
import Vuetify from 'vuetify'
import VBtnPlus from '@/components/common/VBtnPlus.vue'
describe('VStatsCard.vue', () => {
let localVue = null
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuetify)
})
describe('test prop warnings', () => {
let spy = spyConsole()
it('displays warning messages when both label and icon are not specified', () => {
mount(VBtnPlus, {
localVue: localVue
})
expect(console.error).toHaveBeenCalledTimes(1)
expect(spy.console.mock.calls[0][0]).toContain(
'[Vue warn]: Missing required prop, specify at least one of the following: "label" or "icon"'
)
})
})
})
If you came across this post trying to test pino logging, I was able to test my pino configuration with jest by doing the following (credit to How to use Jest to test that pino debug log is written to stdout when load a module in a test?)
import { streamSym } from 'pino/lib/symbols'
import logger from './logger' // where my pino config is
describe('Logger default configuration', () => {
beforeEach(() => {
jest.resetAllMocks()
jest.restoreAllMocks()
})
it('Logs an error', () => {
const spyOnLoggerError = jest.spyOn(logger, 'error')
const spyOnLoggerStreamWrite = jest.spyOn(logger[streamSym], 'write')
const myTestError = new Error('My test error')
logger.error(myTestError)
expect(spyOnLoggerError).toBeCalledWith(myTestError) // verify logger.error called
expect(spyOnLoggerStreamWrite).toBeCalledTimes(1) // verify that logger.error was only called once
expect(spyOnLoggerStreamWrite).toBeCalledWith(expect.stringContaining('"level":50')) // validate log level of message
expect(spyOnLoggerStreamWrite).toBeCalledWith(expect.stringContaining('"message":"My test error"')) // validate that message contains error message
})
})
Make sure when testing that the log level
in the pino config is set to a value that will actually log the result. For instance, my default level
is set to 'error' which is why the test passes. However, I would need to mock my environment variable that sets the level
if I needed to test logger.trace(new Error('Some trace error'))
.
UPDATE:
Note that I previously tried to use jest.spyOn((global.console as any)._stdout, 'write')
, but this proved to be unreliable and failed when I ran all of the tests.
For reference, here is my pino config (./logger.ts
):
// logger.ts
import { isPlainObject } from 'lodash'
import pino, { Logger } from 'pino'
import pinoCaller from 'pino-caller'
import pinoStdSerializers from 'pino-std-serializers'
const normalSerializer = (value): string => {
if (typeof value === 'object') return JSON.stringify(value)
if (typeof value === 'undefined') return 'undefined'
return value
}
const isError = (value): boolean => value instanceof Error
const appName = 'myAppName'
const logLevel = process.env.LOG_LEVEL || 'error'
const pinoLogger = pino({
name: `${appName}`,
level: logLevel,
hooks: {
logMethod(args, method): void {
const arrayArgs = Array.from(args)
arrayArgs.forEach(arg => {
const isArgError = isError(arg)
const doesArgContainErrorValue = isPlainObject(arg) && Object.values(arg).some(val => isError(val))
const isErrorFoundInArg = isArgError || doesArgContainErrorValue // use the error serializer if the argument is an instance of error or prop value is instance of error
const serializer = isErrorFoundInArg ? pinoStdSerializers.err : normalSerializer
method.apply(this, [serializer(arg)])
})
},
},
prettyPrint: process.env.ENABLE_PRETTY_LOG === 'true',
})
/**
* Accepted logger arguments are as follows:
* logger.trace(err) // where the argument err is an instance of Error
* logger.info({ err, myCtx: {prop1: 'some value'} }) // where the argument is an object with err being an instanceof Error and myCtx being some arbitrary context object
* logger.warn({prop1: 1, prop2: 2}) // where the argument is an arbitrary object
* logger.error('My String')
*/
const logger = process.env.ENABLE_LOG_CALL_DETAILS === 'true' ? pinoCaller(pinoLogger) : pinoLogger
export default logger