首页 > 解决方案 > Jest + Mongoose: Have to run find twice to get results

问题描述

I am building tests for my node/express controller methods and using @shelf/jest-mongodb. I am creating a document first, and then when I try to find that I have to run find twice from model in order to get the results. It should get the results in the first find instead.

test.js

const { Subscription } = require('../src/models/subscription.schemaModel'); // model
const {
  createSubscription,
} = require('../src/controllers/subscription.controller');

const subData = {...};

beforeAll(async () => {
  await mongoose.connect(

    process.env.MONGO_URL,
    { useNewUrlParser: true, useUnifiedTopology: true },
    (err) => {
      if (err) {
        console.error(err);
        process.exit(1);
      }
    }
  );
});

afterAll(async () => {
  await mongoose.connection.close();
});

describe('creates a subscription ', () => {
  it('can be created correctly', async () => {
    const sub = await createSubscription(subData);
    await Subscription.find(); // if I comment out this line, I would get 0 results.
    const subs = await Subscription.find();
    expect(subs[0].items[0].sku).toBe(233234);
  });
});

subscription.controller.js

const Mongoose = require('mongoose');
const { Subscription } = require('../models/subscription.schemaModel');
const isTestEnv = process.env.NODE_ENV === 'test';

module.exports.createSubscription = async (data) => {
  try {
    let error = null;
    const doc = new Subscription(data);
    doc.accountId = Mongoose.Types.ObjectId(doc.accountId);

    await doc.save(function (err) {
      if (err) {
        logger.error(`createSubscription saving ${err}`);
        error = err;
      }
    });

    if (!error) {
      logger.info(
        `Subscription created =>  id: ${doc._id} store: ${doc.store}`
      );
      return doc;
    } else {
      return error;
    }
  } catch (err) {
    logger.error(`createSubscription ${err}`);
  }
};

The schemaModel file essentially contains the schema and exports model. Everything seems to work fine if I would do all the operations in the test file (schema+model+controller module)which defeats the purpose of testing my modules but not if I am importing. In this case I would have to run find() twice to get the results.

I have been trying multiple things from what I could find from googling, but no luck! Any help or lead would be appreciated. Also let me know if you need any other details. Thank you!!

标签: node.jsmongodbexpressmongoosejestjs

解决方案


The only problem that posted code contains is that Mongoose promise API is mixed with legacy callback API. It appears that save results in race condition that is has been circumvented by random delay that extra find provides.

Although Mongoose documentation mentions that methods unconditionally return promises, a common pattern for JavaScript APIs that support both promises and callbacks is to enable promise control flow by omitting callback argument, and vice versa. This is most likely what happens here.

A way to avoid race conditions in such cases is to stick to promise control flow, e.g.:

beforeAll(async () => {
 try {
  await mongoose.connect(
    process.env.MONGO_URL,
    { useNewUrlParser: true, useUnifiedTopology: true },
  )
 } catch (err) {
   console.error(err);
   process.exit(1);
 }
});

推荐阅读