Mocha 抛出超时,但 Mongoose 将数据保存到 Promise 链中的数据库?

Posted

技术标签:

【中文标题】Mocha 抛出超时,但 Mongoose 将数据保存到 Promise 链中的数据库?【英文标题】:Mocha throws timeout exceeded, yet Mongoose saves data to DB in Promise chain? 【发布时间】:2019-02-03 19:12:48 【问题描述】:

我正在使用 Mocha、Mongoose 和 MongoDB。

我的目标只是围绕创建帐户进行测试。我有以下代码:

require( "./../../config/config" );
var mongoose = require( "mongoose" );
mongoose.connect( process.env.MONGODB_URI );

const expect = require( "expect" );
var  Account  = require( "./../../models/account" );

describe( "Account Creation", () =>

    it( "should successfully create an account", ( done ) =>
    
        var data =
        
            username: "PrestonJ",
            email: "someEmail@mail.com",
            password: "somePassword123!"
        ;

        var account = new Account( data );
        account.save().then( ( result ) =>
        
            console.log( "(temporary log)  account saved" );
            done();
        ).catch( ( e ) => done( e ) );
    );
);

Mongoose 承诺链执行并将新帐户保存到数据库中,但从未到达 done()(即使它已写入,并且上面的控制台调用有效)。

这导致 Mocha 测试失败,给我: Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.

我试图只使用return Promise 链,但这也不起作用。我还尝试将 done 连同返回链一起删除,但无济于事。

已经好几个小时了,我一直找不到正确的做法/修复方法。

编辑: 我试过增加摩卡的超时时间。错误是否以某种方式被吞没?它在我的模型文件中吗?

编辑 2: 以下是帐户模型的来源:

var mongoose = require( "mongoose" );

const _ = require( "lodash" );
const validator = require( "validator" );
const jwt = require( "jsonwebtoken" );
const bcrypt = require( "bcryptjs" );

let Account_CollectionName = "accounts";
var Account_Schema = new mongoose.Schema(

    username:
    
        type: String,
        minLength: 3,
        trim: true,
        required: true,
        unique: true
    ,
    email:
    
        type: String,
        minlength: 1,
        trim: true,
        required: false,
        unique: true,
        sparse: true,
        validate: validator.isEmail,
        message: "VALUE is not a valid email"
    ,
    password:
    
        type: String,
        required: true,
        minLength: 5
    ,
    tokens:
    [
        access:
        
            type: String,
            required: true
        ,
        token:
        
            type: String,
            required: true
        
    ],
,

    collection: Account_CollectionName
);

Account_Schema.pre( "save", function( next )

    var account = this;

    if( account.isModified( "password" ) )
    
        bcrypt.genSalt( 10, ( err, salt ) =>
        
            bcrypt.hash( account.password, salt, ( err, hash ) =>
            
                account.password = hash;
                next();
            );
        );
    
    else
        next();
);

Account_Schema.methods.toJSON = function()

    var account = this;
    var accountObject = account.toObject();

    return _.pick( accountObject,
        [
            "_id", "username", "email"
        ] );
;

Account_Schema.methods.generateAuthToken = function()

    var account = this;
    var access = "auth";
    var token = jwt.sign(  _id: account._id.toHexString(), access , process.env.JWT_SECRET ).toString();

    account.tokens = account.tokens.concat( [ access, token ] );

    return account.save().then( () =>
    
        return token;
    );
;

Account_Schema.statics.findByToken = function( token )

    var Account = this;
    var decoded;

    try 
        decoded = jwt.verify( token, process.env.JWT_SECRET );
    
    catch( e )
    
        return Promise.reject();
    

    return Account.findOne(
    
        "_id" : decoded._id,
        "tokens.token": token,
        "tokens.access": "auth"
    );
;

Account_Schema.statics.findByCredentials = function( username, password )

    var Account = this;

    return Account.findOne(  username  ).then( ( account ) =>
    
        if( ! account )
            return Promise.reject();

        return new Promise( ( resolve, reject ) =>
        
            bcrypt.compare( password, account.password, ( err, res ) =>
            
                if( res )
                    resolve( account );
                else
                    reject();
            );
        );
    );
;

Account_Schema.methods.removeToken = function( token )

    var account = this;

    return account.update(
    
        $pull:
        
            tokens:  token 
        
    );
;

var Account = mongoose.model( Account_CollectionName, Account_Schema );

module.exports =  Account ;

【问题讨论】:

请发账号源代码 我已经编辑了帖子并添加了帐户源代码 首先,检查数据库连接是否正常。然后,试试account.save().exec().then(...) .exec() 不起作用,我一直收到 .exec 不是函数的错误。但是,我解决了我的问题并发布了解决方案。 【参考方案1】:

我想通了。需要做一些事情;这是it 块的固定来源:

it( "should successfully create an account", () =>

    var data =
    
        username: "PrestonJ",
        email: "someEmail@mail.com",
        password: "somePassword123!"
    ;

    let account = new Account( data );
    return account.save().then( ( result ) =>
    
        if( result )
        
            console.log( "Successful!" );
            return Promise.resolve();
        
        else
            return Promise.reject( "cannot save" );
    ).catch( ( e ) =>
    
        return Promise.reject( e );
    );
);
    删除done/done()的所有痕迹 returnaccount.save().then(...)...then() 中,使用return Promise.resolve() 而不是done()catch() 中,使用return Promise.reject(e) 而不是done(e)

【讨论】:

【参考方案2】:

您收到此错误是因为 mocha 在完成保存操作之前等待了 200 毫秒的时间段,并且由于此操作花费了超过 200 毫秒,因此您的测试失败了。

处理此类问题的最佳方法是了解您的应用响应所需的最长时间并将该时间设置为超时。

您可以通过添加标志 timeout 来设置 mocha 测试的超时时间

你可以在 mocha.opts 文件中提供这个

    --timeout 50000

这将使您的测试用例在任何操作超时之前等待 50 秒。

【讨论】:

这不太可能是错误,保存一个文档需要几毫秒,2000ms 已经足够了。 我已尝试将其增加很多。我认为一个错误正在被吞下。 你可以尝试在account.save().then之前添加return

以上是关于Mocha 抛出超时,但 Mongoose 将数据保存到 Promise 链中的数据库?的主要内容,如果未能解决你的问题,请参考以下文章

使用 mocha 测试时超时 [重复]

尽管使用 done,Mocha 在 Before 钩子中调用异步承诺链超时

如何在 mocha 单元测试中使用猫鼬?

将变量传递给 Mocha 测试套件

测试 Mocha 中抛出的错误 [重复]

伊斯坦布尔封面报告对于使用 mocha 进行测试是错误的(使用 Mongoose)