Dycey
Dycey

Reputation: 4685

Attempting to use async/await over an array function

I'm trying to simplify my test scaffolding for an Express backend. Rather than call a single record insert routine a number of times, which seems to have sync issues when checking the result, I want to perform the db inserts one after the other, using the array.

Here's what I have so far... but I seem to be screwing about the async await somehow. The tests fail as I get 0 body lengths and maybe one insertion. Any ideas where I'm going wrong?


async function asyncForEach (array, callback) {
  for (let index = 0; index < array.length; index++) {
    await callback(array[index], index, array);
  };
}

async function insertTemplates(myTemplatesArray) {
  const client = new Client(dbConfig);
  client.connect();
  await asyncForEach(
    myTemplatesArray,
    async (myTemplate) => {
      let { name, status, version } = myTemplate;
      await client.query(
        SQL`INSERT INTO templates (name, status, version )
        VALUES (${name}, ${status}, ${version})`)
        .catch( err => console.error(err) );
    });
  client.end();
}

function randomTemplate() {
  const template = {
    name: faker.internet.userName(),
    status: faker.hacker.phrase(),
    version: String(faker.random.number())
  };
  return template;
}

function clean_db(){
  const client = new Client(dbConfig);
  client.connect();
  client.query(
    `TRUNCATE TABLE "templates";
    ALTER SEQUENCE "templates_id_seq" RESTART WITH 1;`)
    .catch( err => { console.error(err); } )
    .then( () => client.end() );
}

describe('When there are 1 or more records in the table', () => {

    const templ1 = randomTemplate();
    const templ2 = randomTemplate();
    const templ3 = randomTemplate();

    beforeEach(async () => {
      await clean_db();
      await insertTemplates([templ1,templ2,templ3]);
    });

    afterEach(async () => {
      await clean_db();
    });

    test('It should respond with a 200 status', async done => {
      const response = await template(app).get('/templates');
      expect(response.status).toBe(200);
      done();
    });

    test('It should respond with an JSON array of records', async done => {
      const response = await template(app).get('/templates');
      expect(response.body.length).toBe(3);
      done();
    });

    test('It should respond with the correct records', async done => {
      templ1.id = 1;
      templ2.id = 2;
      templ3.id = 3;

      const response = await template(app).get('/templates');
      expect(response.body).toStrictEqual([templ1,templ2,templ3]);
      done();
    });
  });
});

Upvotes: 2

Views: 140

Answers (1)

Terry Lennox
Terry Lennox

Reputation: 30695

I believe the reason your test isn't working as expected is that you're breaking the promise chain in clean_db().. and perhaps a few other functions. This is very easy to do this when working with promises!

I'd suggest making sure clean_db returns a promise, e.g. like so:

function clean_db(){
    const client = new Client(dbConfig);
    client.connect();
    // return promise to keep chain intact.
    return client.query(
        `TRUNCATE TABLE "templates";
        ALTER SEQUENCE "templates_id_seq" RESTART WITH 1;`)
        .catch( err => { console.error(err); } )
        .then( () => client.end() );
}

I'm presuming that client.end() returns something.. this should cause clean_db() to return a promise so the await call will actually await.

I'd do the same for insertTemplates, e.g. return the result of client.end().

async function insertTemplates(myTemplatesArray) {
  const client = new Client(dbConfig);
  client.connect();
  await asyncForEach(
    myTemplatesArray,
    async (myTemplate) => {
      let { name, status, version } = myTemplate;
      // Return promise to calling proc.
      return client.query(
        SQL`INSERT INTO templates (name, status, version )
        VALUES (${name}, ${status}, ${version})`)
        .catch( err => console.error(err) );
    });
    return client.end();
}

Also, make sure that the callback you pass to asyncForEach returns a promise.. I'm not sure if it does.

Upvotes: 1

Related Questions