Mongoose aggregation query fails in Jest/Mockgoose

2019-01-28 06:42发布

问题:

I'm trying to create some tests for my Mongoose models, and I can't figure out how to get the Jest/Mockgoose test to pass for a shorthand query/aggregation pipeline (see below in first code block) that I created to retrieve a random document from one collection that isn't referenced in another collection.

activitySchema.query.getUnused = function() {
    return Activity.aggregate()
        .lookup({
            from: 'userActivity',
            localField: '_id',
            foreignField: 'activity',
            as: 'matched_docs',
        })
        .match({ matched_docs: { $eq: [] } })
        .sample(1)
        .project({ matched_docs: 0, __v: 0 })
        .exec()
}

Jest test

describe('Activity methods', () => {
    test('Activity unused query executes', (done) => {
        function createActivity() {
            return Activity
                .create({ activity: 'running' })
                .then(activity => {
                    console.log('Activity is generated')
                    return Promise.resolve(true)
                })
        }
        async function retrieveActivity() {
            await createActivity()
            Activity
                .find()
                .getUnused()
                .then(unused => {
                    console.log(unused);
                    expect(unused).toHaveLength(1)
                    done()
                })
                .catch(x => {
                console.log(x)
                })
        }


        return retrieveActivity()
    })

})

Sandbox Node JS code:

Activity.find().getUnused()
    .then((x) => {
     console.log(x);
})

When I try it in a sandbox node file, wit works normally and retrieves a typical queryset like:

   [ { _id: 58f3dee3b0346910a69e6e5d, activity: 'running', __v: 0 } ]

When I run the test, I get this MongoError:

The 'cursor' option is required, except for aggregation explain

How do I fix this with a universal manner that works in both contexts? I'd like for the method to return a promise if possible. I've tried both the chained aggregate methods (see above) and the more native Mongo aggregate array for pipelines, but both return errors. My mongo version is 3.4.2 if that's relevant.

回答1:

Finally figured this out: I had to add a cursor call to the aggregation pipeline, and translate it into a stream. To maintain the Promise I had the query method return a Promise that resolves with data once the stream has ended, as below:

activitySchema.query.getUnused = function() {
    return new Promise((res, rej) => {
        let data = []
        return Activity.aggregate()
            .lookup({
                from: 'userActivity',
                localField: '_id',
                foreignField: 'activity',
                as: 'matched_docs',
            })
            .match({ matched_docs: { $eq: [] } })
            .sample(1)
            .project({ matched_docs: 0, __v: 0 })
            .cursor({})
            .exec()
            .on('data', doc => data.push(doc))
            .on('end', () => res(data))

    })
}


回答2:

I have faced similar kind of problem and after researching a lot I found that that it was due to older version of mongoose as earlier versions are not compatible with breaking changes in MongoDB 3.6 and above.

I upgraded mongoose version one by one and I found that it works perfectly fine with mongoose version 4.12.2 or above (mongoose@4.12.2).

You can upgrade your mongoose version by running following command:

npm install mongoose@4.12.2


回答3:

I faced similar issue with the mongodb 3.6 and mongoose 4.x then I just updated my mongoose version from 4.x to 5.x.

npm install mongoose@5.2.13

And the above resolved the issue.