测试成功完成后开玩笑没有终止
Posted
技术标签:
【中文标题】测试成功完成后开玩笑没有终止【英文标题】:Jest not terminating after tests complete successfully 【发布时间】:2017-10-17 14:02:52 【问题描述】:我正在使用jest
& supertest
来测试我的 api 端点,并且测试顺利通过;但是jest
永远不会退出我通常会看到的 Done in X amount of time 语句。我查看以确保没有使用 --watch
标志,但事实并非如此。似乎与服务器的连接永远不会结束,所以jest
不知道接下来会发生什么。 UserFactory.generate()
使用 faker
库创建虚假用户数据。
我在尝试解决此问题时不知所措。我在jest
的帮助页面上遵循了推荐的策略,但没有运气,也一直在问题跟踪器中进行挖掘,但对类似问题一无所知。
这是我在运行我的测试套件时会看到的:
如您所见,测试运行了 7 次。我被告知所有测试都已运行,然后morgan
显示已发生的POST
。 jest
从不退出,所以实际上这是失败的,因为它会在任何 CI 服务器上超时,除非手动退出。
我尝试使用.end() & done()
和.then() & done()
代替async/await
。他们都返回相同的最终结果,如果这是一个未解决的承诺问题 jest
将在未解决的承诺上出错,所以我很难理解为什么这不会像 jest
通常那样终止。
以前有人遇到过这样的问题吗?
user.controller.test.js
import mongoose from 'mongoose';
import request from 'supertest';
import UserFactory from '../../__mocks__';
import User from '../../modules';
import config from '../../utils';
import app from '../../';
const mockRoute = data => request(app).post(`$config.ENDPOINT/user/sign-up`).send(data);
describe(`POST: /user/sign-up`, () =>
// remove any user data from db prior to running tests.
beforeAll(async () => await User.remove(); );
test('Returns status 201 on success.', async () =>
// Returns the response object: res.status === status
const status = await mockRoute(UserFactory.generate());
expect(status).toEqual(201);
);
afterAll(async () =>
// drop connection to the collection
const users = mongoose.connection.collections;
await users.drop();
);
);
user/routes.js
import Router from 'express';
import validate from 'express-validation';
import signUp from './controller';
import valid from './validation'
const routes = new Router();
/**
* 1. Define the route: 'user/signup'.
* 2. Validate the data being provided on the POST
* against valid.signUp object.
* 3. Provide data to signUp controller method for
* creating a user in the database.
*/
routes.post('/user/sign-up', validate(valid.signUp), signUp);
export default routes;
user/controller.js
import HTTPStatus from 'http-status';
import User from './model';
import config, filterBody from '../../utils';
export const signUp = async (req, res, next) =>
const filteredBody = filterBody(req.body, config.WHITELIST.users.signUp);
try
const user = await User.create(filteredBody);
return res.status(HTTPStatus.CREATED).json(user.toAuthJSON());
catch (e)
e.status = HTTPStatus.BAD_REQUEST;
return next(e);
user/model.js
import mongoose, Schema from 'mongoose';
import uniqueValidator from 'mongoose-unique-validator';
import hashSync, compareSync from 'bcrypt-nodejs';
import jwt from 'jsonwebtoken';
import config from '../../utils';
const UserSchema = new Schema(
email:
type: String,
unique: true,
required: [true, 'Email is required!'],
trim: true,
validate:
validator(email)
const emailRegex = /^[-a-z0-9%S_+]+(\.[-a-z0-9%S_+]+)*@(?:[a-z0-9-]1,63\.)1,125[a-z]2,63$/i;
return emailRegex.test(email);
,
message: 'VALUE is not a valid email!',
,
password:
type: String,
required: [true, 'Password is required!'],
trim: true,
minlength: [6, 'Password need to be longer!'],
validate:
validator(password)
return password.length >= 6 && password.match(/\d+/g);
,
,
, timestamps: true )
UserSchema.plugin(uniqueValidator,
message: 'VALUE already taken!',
);
UserSchema.pre('save', function(next)
if (this.isModified('password'))
this.password = this._hashPassword(this.password);
return next();
return next();
);
UserSchema.methods =
authenticateUser(password)
return compareSync(password, this.password);
,
_hashPassword(password)
return hashSync(password);
,
createToken()
return jwt.sign( _id: this._id , config.JWT_SECRET);
,
toAuthJSON()
return
_id: this._id,
token: `JWT $this.createToken()`,
;
,
toJSON()
return
_id: this._id,
username: this.username,
;
,
;
let User;
try
User = mongoose.model('User');
catch (e)
User = mongoose.model('User', UserSchema);
export default User;
user/validation.js
import Joi from 'joi';
export default
signUp:
body:
email: Joi.string().email().required(),
password: Joi.string()
.min(6)
.regex(/^(?=.*[0-9])(?=.*[a-zA-Z])([a-zA-Z0-9]+)$/)
.required(),
,
,
;
middlewares.js
import bodyParser from 'body-parser';
import compression from 'compression';
import cors from 'cors';
import morgan from 'morgan';
import userRoutes from '../modules';
export default app =>
app.use(compression());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded( extended: true ));
app.use(cors());
app.use(morgan('dev'));
// applying api routes last.
app.use('/api/v1', [userRoutes]);
index.js
import express from 'express';
import
database,
config,
middlewares,
from './utils';
// Create instance of Express.
const app = express();
const MODE = process.env.NODE_ENV;
// Apply middlewares to Express.
middlewares(app);
// Connect to the environment determined database.
database(config.MONGO_URI)
app.listen(config.PORT, err =>
if (err) return console.error(err);
console.log(`App running on port: $config.PORT in $MODE mode.`);
);
export default app;
【问题讨论】:
【参考方案1】:发现问题在于我在 afterAll()
挂钩中做得不够。
我创建了一个小型 repo 来重现问题并从那里解决问题,这是需要做的,以便 jest
可以在成功完成测试套件后退出:
afterAll(async () =>
try
const todos = mongoose.connection.collections;
// Collection is being dropped.
await todos.drop()
// Connection to Mongo killed.
await mongoose.disconnect();
// Server connection closed.
await server.close();
catch (error)
console.log(`
You did something wrong dummy!
$error
`);
throw error;
【讨论】:
我还想补充一点,这里不需要unlock()
模块。这只是数据库连接仍处于打开状态并阻止 jest
以 0 退出的问题。
对我来说 await mongoose.disconnect();
就足够了。我没有运行server.close();
。此外,如果其他人(比如我)有多个连接(使用createConnection()
而不是connect()
打开),您只需要一个mongoose.disconnect();
而不是connection1.disconnect()
和conneciton2.disconnect()
【参考方案2】:
这对我有用:
afterAll(async () =>
try
// Connection to Mongo killed.
await mongoose.disconnect();
catch (error)
console.log(`
You did something wrong dummy!
$error
`);
throw error;
);
【讨论】:
以上是关于测试成功完成后开玩笑没有终止的主要内容,如果未能解决你的问题,请参考以下文章