在单元测试中禁用控制台的更好方法

我想知道是否有更好的方法禁用具体的 Jest测试中的控制台错误(即在每次测试之前/之后恢复原始控制台)。

以下是我目前的方法:

describe("Some description", () => {
let consoleSpy;


beforeEach(() => {
if (typeof consoleSpy === "function") {
consoleSpy.mockRestore();
}
});


test("Some test that should not output errors to jest console", () => {
expect.assertions(2);


consoleSpy = jest.spyOn(console, "error").mockImplementation();
 

// some function that uses console error
expect(someFunction).toBe("X");
expect(consoleSpy).toHaveBeenCalled();
});


test("Test that has console available", () => {
// shows up during jest watch test, just as intended
console.error("test");
});
});

有没有更干净的方式来完成同样的事情?我想避免spyOn,但mockRestore似乎只适用于它。

125661 次浏览

由于每个测试文件都在自己的线程中运行,因此如果您想在一个文件中禁用所有测试,则不需要恢复它。出于同样的原因,你也可以只写

console.log = jest.fn()
expect(console.log).toHaveBeenCalled();

对于特定的规格文件,Andreas的已经足够好了。下面的设置将抑制所有测试套件的console.log语句,

jest --silent

(或)

要自定义warn, info and debug,您可以使用下面的设置

< >强测试/setup.js jest-preload.jssetupFilesAfterEnv 中配置

global.console = {
...console,
// uncomment to ignore a specific log level
log: jest.fn(),
debug: jest.fn(),
info: jest.fn(),
// warn: jest.fn(),
// error: jest.fn(),
};

jest.config.js

module.exports = {
verbose: true,
setupFilesAfterEnv: ["<rootDir>/__tests__/setup.js"],
};

我发现上面的答案是:在所有测试套件中抑制console.log会在调用任何其他console方法(例如warnerror)时抛出错误,因为它正在替换整个全局console对象。

这种有点类似的方法适用于我的Jest 22+:

package.json

"jest": {
"setupFiles": [...],
"setupTestFrameworkScriptFile": "<rootDir>/jest/setup.js",
...
}

jest / setup.js

jest.spyOn(global.console, 'log').mockImplementation(() => jest.fn());

使用此方法,只有console.log被模拟,其他console方法不受影响。

另一种方法是使用process.env.NODE_ENV通过这种方式,在运行测试时可以选择性地选择显示(或不显示)什么:

if (process.env.NODE_ENV === 'development') {
console.log('Show output only while in "development" mode');
} else if (process.env.NODE_ENV === 'test') {
console.log('Show output only while in "test" mode');
}

const logDev = msg => {
if (process.env.NODE_ENV === 'development') {
console.log(msg);
}
}
logDev('Show output only while in "development" mode');

这将要求将此配置放在package.json上:

"jest": {
"globals": {
"NODE_ENV": "test"
}
}

注意,这种方法不是原始问题的直接解决方案,但只要有可能用上述条件包装console.log,就会给出预期的结果。

对我来说,一个更清晰/干净的方式(读者需要很少的笑话API知识来理解发生了什么),就是手动做mockRestore所做的事情:

// at start of test you want to suppress
const consoleLog = console.log;
console.log = jest.fn();


// at end of test
console.log = consoleLog;

如果你只是想做一个特定的测试:

beforeEach(() => {
jest.spyOn(console, 'warn').mockImplementation(() => {});
});
beforeAll(() => {
jest.spyOn(console, 'log').mockImplementation(() => {});
jest.spyOn(console, 'error').mockImplementation(() => {});
jest.spyOn(console, 'warn').mockImplementation(() => {});
jest.spyOn(console, 'info').mockImplementation(() => {});
jest.spyOn(console, 'debug').mockImplementation(() => {});
});

向@Raja的最佳答案致敬。这是我正在使用的(我会注释,但不能在注释中共享多行代码块)。

与jest v26,我得到这个错误:

We detected setupFilesAfterEnv in your package.json.


Remove it from Jest configuration, and put the initialization code in src/setupTests.js:
This file will be loaded automatically.

因此,我必须从我的笑话配置中删除setupFilesAfterEnv,并将其添加到src/setupTests.js

// https://stackoverflow.com/questions/44467657/jest-better-way-to-disable-console-inside-unit-tests
const nativeConsoleError = global.console.error


global.console.error = (...args) => {
if (args.join('').includes('Could not parse CSS stylesheet')) {
return
}
return nativeConsoleError(...args)
}

由于jest.spyOn不适用于此(它在过去可能适用),我求助于jest.fn,并在Jest文档中指出手动模拟恢复。这样,您就不会错过任何在特定测试中没有被经验忽略的日志。

const consoleError = console.error


beforeEach(() => {
console.error = consoleError
})


test('with error', () => {
console.error = jest.fn()
console.error('error') // can't see me
})


test('with error and log', () => {
console.error('error') // now you can
})

奇怪的是,上面的答案(除了拉贾的伟大答案,但我想分享其他失败的奇怪方式,以及如何清除模拟,这样其他人就不会浪费我的时间)似乎成功地创建了模拟,但抑制了控制台的日志记录。

这两个

const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});

而且

global console = {
warn: jest.fn().mockImplementation(() => {});
}

成功安装模拟(我可以使用expect(console.warn). tobecalledtimes(1)并且它通过),但它仍然输出警告,即使模拟实现似乎应该替换默认值(这是在jsdom环境中)。

最终,我找到了一个解决问题的黑客,并将以下内容放在配置中SetupFiles加载的文件中(注意,我发现有时是全局的。$不为我工作时,把jquery到全局上下文中,所以我只是设置我所有的全局在我的设置)。

const consoleWarn = jest.spyOn(console, 'warn').mockImplementation(() => {});
const consoleLog = jest.spyOn(console, 'log').mockImplementation(() => {});
const consoleDebug = jest.spyOn(console, 'debug').mockImplementation(() => {});
const consoleError = jest.spyOn(console, 'error').mockImplementation(() => {});




Object.defineProperty(global, 'console', {value: {
warn: consoleWarn,
log: consoleLog,
debug: consoleDebug,
error: consoleError}});

它感觉很难看,然后我不得不把如下代码放在每个测试文件中,因为beforeEach没有在SetupFiles引用的文件中定义(也许你可以把两者都放在SetupFilesAfterEnv中,但我没有尝试过)。

beforeEach(() => {
console.warn.mockClear();
});

如果你正在使用命令npm test来运行测试,那么在package.json中更改test script,如下所示

{
....
"name": "....",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest --silent",         // add --silent to jest in script like this
"lint": "eslint ."
},
...
}

或者,您可以直接运行命令npx jest --silent来清除测试时的所有日志和错误

这里是你可能会用到的所有台词。你可以把它们放在测试中:

jest.spyOn(console, 'warn').mockImplementation(() => {});
console.warn("You won't see me!")
expect(console.warn).toHaveBeenCalled();
console.warn.mockRestore();