使用 Jest 测试 Mongoose
¥Testing Mongoose with Jest
Jest 是 Facebook 开发的 JavaScript 运行时,通常用于测试。由于 Jest 主要是为了测试 React 应用而设计的,因此使用它来测试 Node.js 服务器端应用有很多注意事项。我们强烈建议使用不同的测试框架,例如 Mocha。
¥Jest is a JavaScript runtime developed by Facebook that is usually used for testing. Because Jest is designed primarily for testing React applications, using it to test Node.js server-side applications comes with a lot of caveats. We strongly recommend using a different testing framework, like Mocha.
要禁止 Mongoose 发出任何 Jest 警告,请设置 SUPPRESS_JEST_WARNINGS
环境变量:
¥To suppress any Jest warnings from Mongoose, set the SUPPRESS_JEST_WARNINGS
environment variable:
env SUPPRESS_JEST_WARNINGS=1 npm test
如果你选择深入危险字段并使用 Jest 测试 Mongoose 应用,那么你需要了解以下内容:
¥If you choose to delve into dangerous waters and test Mongoose apps with Jest, here's what you need to know:
推荐 testEnvironment
¥Recommended testEnvironment
如果你使用 Jest <=26
,则在测试 Mongoose 应用时不要使用 Jest 的默认 jsdom
测试环境,除非你明确测试仅使用 Mongoose 的浏览器库 的应用。在 Jest >=27
、"node" 是 Jest 的默认 testEnvironment
中,所以这不再是问题。
¥If you are using Jest <=26
, do not use Jest's default jsdom
test environment when testing Mongoose apps, unless you are explicitly testing an application that only uses Mongoose's browser library. In Jest >=27
, "node" is Jest's default testEnvironment
, so this is no longer an issue.
jsdom
测试环境试图在 Node.js 中创建一个类似浏览器的测试环境,它带来了许多令人讨厌的意外,就像 存根 setTimeout()
函数 在测试完成后默默地失败一样。Mongoose 一般不支持 jsdom,预计在 jsdom
测试环境中无法正常运行。
¥The jsdom
test environment attempts to create a browser-like test
environment in Node.js, and it comes with numerous nasty surprises like a
stubbed setTimeout()
function
that silently fails after tests are finished. Mongoose does not support jsdom
in general and is not expected to function correctly in the jsdom
test
environment.
要将 testEnvironment
更改为 Node.js,请将 testEnvironment
添加到 jest.config.js
文件中:
¥To change your testEnvironment
to Node.js, add testEnvironment
to your
jest.config.js
file:
module.exports = {
testEnvironment: 'node'
};
定时器模拟
¥Timer Mocks
测试 Mongoose 应用时绝对不要使用 计时器模拟。如果你使用 Jest >=25
(它会淘汰 process.nextTick()
),这一点尤其重要。
¥Absolutely do not use timer mocks when testing Mongoose apps.
This is especially important if you're using Jest >=25
, which stubs out process.nextTick()
.
假定时器会屏蔽 setTimeout()
和 setInterval()
等全局函数,这会在底层库使用这些函数时导致问题。Mongoose 和 MongoDB Node.js 驱动程序使用这些函数将工作推迟到事件循环的下一个周期,并监视与 MongoDB 服务器的连接。
¥Fake timers stub out global functions like setTimeout()
and setInterval()
, which causes problems when an underlying library uses these functions.
Mongoose and the MongoDB Node.js driver uses these functions for deferring work until the next tick of the event loop and for monitoring connections to the MongoDB server.
如果你绝对必须使用计时器模拟,请确保在调用 useFakeTimers()
之前导入 Mongoose:
¥If you absolutely must use timer mocks, make sure you import Mongoose before calling useFakeTimers()
:
// Fine for basic cases, but may still cause issues:
const mongoose = require('mongoose');
jest.useFakeTimers();
// Bad:
jest.useFakeTimers();
const mongoose = require('mongoose');
Mongoose 开发者已经将代码重构为 避免使用 setImmediate()
,以将工作推迟到事件循环的下一个周期,但我们无法合理确保 Mongoose 依赖的每个库都不使用 setImmediate()
。
¥Mongoose devs have already refactored out code to avoid using setImmediate()
to defer work to the next tick of the event loop, but we can't reasonably ensure that every library Mongoose depends on doesn't use setImmediate()
.
更好的选择是围绕 setTimeout()
和存根创建你自己的封装器,而不是使用 sinon。
¥A better alternative is to create your own wrapper around setTimeout()
and
stub that instead using sinon.
// time.js
exports.setTimeout = function() {
return global.setTimeout.apply(global, arguments);
};
// Tests
const time = require('../util/time');
const sinon = require('sinon');
sinon.stub(time, 'setTimeout');
globalSetup
和 globalTeardown
¥globalSetup
and globalTeardown
请勿使用 globalSetup
调用 mongoose.connect()
或 mongoose.createConnection()
。Jest 在 独立的环境 中运行 globalSetup
,因此你无法在测试中使用在 globalSetup
中创建的任何连接。
¥Do not use globalSetup
to call mongoose.connect()
or
mongoose.createConnection()
. Jest runs globalSetup
in
a separate environment,
so you cannot use any connections you create in globalSetup
in your tests.
resetModules
我们建议在 Jest 配置中将 resetModules
设置为 false
。通过创建 Mongoose 模块的多个悬空副本来实现 resetModules: true
可能导致内部 instanceof
检查出现问题。
¥We recommend setting resetModules
to false
in your Jest config.
resetModules: true
can cause issues with internal instanceof
checks by creating multiple dangling copies of the Mongoose module.
进一步阅读
¥Further Reading
想要了解如何正确测试 Mongoose 应用?Pluralsight 上的 使用 Node.js 和 Express 的 RESTful Web 服务 教程有一个关于使用 Mocha 测试 Mongoose 应用的精彩部分。
¥Want to learn how to test Mongoose apps correctly? The RESTful Web Services with Node.js and Express course on Pluralsight has a great section on testing Mongoose apps with Mocha.
