0

I am trying to write test code for a project management software using Jest. The software is written with Javascript, and uses MongoDB for the database. The project uses an object model hirecarchy that goes:

User => Project => Backlog => Story => Task

I use an external script to populate the test database before running the tests in the test file using a beforeEach block. So far, the populate script makes a couple of users. Then assigns a couple of projects to a chosen user. Then assigns a couple of backlogs to a chosen project. Then assigns a couple of stories to a chosen backlog.

This method has worked for the tests on the user to the backlog model. Now I am writing the test for the story model and I am running into a problem where by the time the code in the test block is executing, the test database is not completely populated.

I have used breakpoints and MongoDBCompass to see what is in the database by the time the code is in the test block, and it appears that database is populated to varying extents during it run. It seems as though the code populating the database is lagging jests execution queue. Is there anyway I can ensure the database population is done before I enter the test block?

Before each block in the story model test file

beforeEach( async () => {
    await User.deleteMany();
    await Project.deleteMany();
    await Backlog.deleteMany();
    await Story.deleteMany();
    populate.makeUsers();
    populate.assignProjects(userXId, userX.userName);
    populate.assignBacklogs(populate.projects[1]);
    await populate.assignStories();
    await new User(userX).save();
});

The function populating the database with Stories

this.assignStories = async function () {
        const pBacklog = await Backlog.findOne({project: this.projects[1]._id, name: "Product Backlog"})

        const temp = this

        if (pBacklog != undefined) {
            let pBacklogID = pBacklog._id
            this.stories = createStories(14, this.projects[1]._id, pBacklogID, this.backlogs[0]._id);
            this.stories.forEach(async (story, index) =>{
                let newStory = new Story(story);
                this.stories[index] = newStory;
                await newStory.save();
            })
        } else {
            setTimeout(async function () {await temp.assignStories()}, 200)
        }
    }

I have excluded the functions for populating the other models to keep this short but I can add it if it will help with the problem.

Thank you.

  • Don't pass a function that accepts a `done` callback if you intend to return a promise (which an `async` function does) - the parameter is a signal to jest that you'll call it, but you never do. – Bergi Jun 15 '21 at 20:59
  • What do `makeUsers`, `assignProjects`, and `assignBacklogs` do - are they asynchronous and would need to be awaited? – Bergi Jun 15 '21 at 21:01
  • [Do not use `forEach` with an asynchronous callback](https://stackoverflow.com/q/37576685/1048572)! – Bergi Jun 15 '21 at 21:02
  • You never wait for the timeout - the function finishes immediately after the `setTimeout` call. Passing an `async function` as the callback to `setTimeout` is pointless. Instead, promisify it, and call `assignStories` recursively *after* `await`ing the timeout. But this whole "polling for the `pBacklog`" pattern is sketchy anyway. – Bergi Jun 15 '21 at 21:04
  • @Bergi the functions are similar to assignStories. They create mongoose objects then ask mongoose to save the objects in the database. They are asynchronous because saving to a database just had to be asynchronous from my understanding of MongoDB – AkalaMangala Jun 16 '21 at 04:49
  • @Bergi How do I promisify the function – AkalaMangala Jun 16 '21 at 04:51
  • If they are asynchronous, they should return a promise that fulfills when they're done, and you need to `await` them. Don't have `assignStories` wait for the objects to appear. That way, you don't even need to [promisify `setTimeout`](https://stackoverflow.com/q/33289726/1048572) – Bergi Jun 16 '21 at 09:03

1 Answers1

0

Thank you @Bergi. The problem was with using forEach with asyn/await functions. I refactored my code using Do not use forEach with an asynchronous callback.