How to deal with Schema.save delay in mongoose tests

Viewed 273

I have a mongoose schema method that inserts a new record then returns an activation code. In my tests I want to call this method then run a query that verifies some things about the record it has added. The problem is the method returns before the record has become visible to the subsequent query, even though I am awaiting the .save before returning.

I have verified this by adding a delay between the method call and the subsequent query. Without the delay the second query returns null. With a delay of above ~30ms the query returns what I was expecting and the test passes.

I don't think such a short delay will cause any problems in production but how can I account for it in my tests without resorting to ugly and arbitrary setTimouts? Is there a way to make the call to .save run and return synchronously in my tests? Or is there an event I can listen for which fires when the data has been written. Or am I thinking about this all wrong?

// My method
userSchema.statics.addNewUser = async function (params) {
    const randomSlug = cryptoRandomString({ length: 64, type: "url-safe" })
    const hashedPassword = await bcrypt
        .genSalt(10)
        .then(salt => bcrypt.hash(params.password, salt))
    const user = new User({
        name: params.name,
        email: params.email,
        password: hashedPassword,
        active: false,
        activationCode: randomSlug
    })
    await user.save((err) => { if (err) throw(err)})
    return randomSlug
}


// My test
it("Should create a new account and return an activation code", async () => {
    const activationCode = await User.addNewUser({
        name: "Bob",
        email: "Bob@iscool.com",
        password: "password",
    })
    assert.match(activationCode, /^.{64}$/)

    // Result is null unless there is a delay of ~30ms here
    const query = User.findOne({ activationCode })
    query.select("name email active password")
    const result = await query.exec()
    console.log("RESULT IS:", result)
    assert(result)

})

1 Answers

Mongoose supports a dual promise and callback API.

Supplying a callback function to Model.prototype.save() returns undefined instead of a Promise, so the await returns immediately.

await user.save()

Rejected promises will be thrown automatically by the async functions.

Related