简体   繁体   中英

Function Defined in Before Block Not Finishing Before Tests Run in Mocha

I am creating some tests using Mocha/Chai in my Node project. In one particular test I need to first save a document to the database, then later retrieve it and run some checks on the found document.

The problem I'm running into is that the it checks are running before the document is finished being saved to the database. I don't understand why this is happening because I am doing the saving in the before() block -- which, according to the Mocha documentation, should run before the tests in that same describe block, see below from the documentation:

> With its default "BDD"-style interface, Mocha provides the hooks
> before(), after(), beforeEach(), and afterEach(). These should be used
> to set up preconditions and clean up after your tests.
> 
> describe('hooks', function() {   before(function() {
>     // runs before all tests in this block   });
> 
>   after(function() {
>     // runs after all tests in this block   });
> 
>   beforeEach(function() {
>     // runs before each test in this block   });
> 
>   afterEach(function() {
>     // runs after each test in this block   });
> 
>   // test cases });

To clarify, when I run the test a second time (and all subsequent times), all checks pass, because by then it can find the document in the db.

What am I missing here?

Here is my code:

const assert = require("chai").assert;
const expect = require("chai").expect;
const chai = require("chai");
chai.use(require("chai-datetime"));
const Agenda = require('agenda');

const config = require('./../../configuration');
const url = config.get('MONGO_URL');
const dbName = config.get('MONGO_DATABASE');
const collection = config.get('MONGO_COLLECTION');
const createAgendaJob = require('./../../lib/agenda-jobs/contact-firstname-to-proper-case');

const MongoClient = require('mongodb').MongoClient;
const client = new MongoClient(url);

describe("Contact FirstName to Proper Case", async function () {
  const jobName = "Contact FirstName To Proper Case";
  const testDate = new Date(2019, 01, 01);
  let result;
  let agenda;
  this.timeout(2000);
  before(async function () {
    const connectionOpts = {
      db: {
        address: `${url}/${dbName}`,
        collection
      }
    };

    agenda = new Agenda(connectionOpts);
    await new Promise(resolve => agenda.once('ready', resolve));
    await createAgendaJob(agenda); // Here is where I save the job to the DB
  });
  client.connect(async function (err) {
    assert.equal(null, err);

    const db = await client.db(dbName);

    result = await db.collection("jobs").findOne({
      "name": jobName
    });

    client.close();
  });
  it("should have a property 'name'", function () {
    expect(result).to.have.property("name");
  });
  it("should have a 'name' of 'Contact FirstName To Proper Case'", function () {
    expect(result.name).to.equal("Contact FirstName To Proper Case");
  });
  it("should have a property 'type'", function () {
    expect(result).to.have.property("type");
  });
  it("should have a 'type' of 'normal'", function () {
    expect(result.type).to.equal("normal");
  });
  it("should have a property 'repeatTimezone'", function () {
    expect(result).to.have.property("repeatTimezone");
  });
  it("should have a property 'repeatInterval'", function () {
    expect(result).to.have.property("repeatInterval");
  });
  it("should have a property 'lastModifiedBy'", function () {
    expect(result).to.have.property("lastModifiedBy");
  });
  it("should have a property 'nextRunAt'", function () {
    expect(result).to.have.property("nextRunAt");
  });
  it("should return a date for the 'nextRunAt' property", function () {
    assert.typeOf(result.nextRunAt, "date");
  });
  it("should 'nextRunAt' to be a date after test date", function () {
    expect(result.nextRunAt).to.afterDate(testDate);
  });
});

I also tried putting the it checks in a subsequent describe() block, but this also didn't work (same result -- doc is only found on SECOND run of the test file):

const assert = require("chai").assert;
const expect = require("chai").expect;
const chai = require("chai");
chai.use(require("chai-datetime"));
const Agenda = require('agenda');

const config = require('./../../configuration');
const url = config.get('MONGO_URL');
const dbName = config.get('MONGO_DATABASE');
const collection = config.get('MONGO_COLLECTION');
const createAgendaJob = require('./../../lib/agenda-jobs/contact-firstname-to-proper-case');

const MongoClient = require('mongodb').MongoClient;
const client = new MongoClient(url);

describe("Contact FirstName to Proper Case", async function () {
  const jobName = "Contact FirstName To Proper Case";
  const testDate = new Date(2019, 01, 01);
  let result;
  let agenda;
  this.timeout(2000);
  before(async function () {
    const connectionOpts = {
      db: {
        address: `${url}/${dbName}`,
        collection
      }
    };

    agenda = new Agenda(connectionOpts);
    await new Promise(resolve => agenda.once('ready', resolve));
    await createAgendaJob(agenda); // Here is where I save the job to the DB
  });
  client.connect(async function (err) {
    assert.equal(null, err);

    const db = await client.db(dbName);

    result = await db.collection("jobs").findOne({
      "name": jobName
    });

    client.close();
  });
  describe("Check Contact FirstName To ProperCase Found Job", function () {
    it("should have a property 'name'", function () {
      expect(result).to.have.property("name");
    });
    it("should have a 'name' of 'Contact FirstName To Proper Case'", function () {
      expect(result.name).to.equal("Contact FirstName To Proper Case");
    });
    it("should have a property 'type'", function () {
      expect(result).to.have.property("type");
    });
    it("should have a 'type' of 'normal'", function () {
      expect(result.type).to.equal("normal");
    });
    it("should have a property 'repeatTimezone'", function () {
      expect(result).to.have.property("repeatTimezone");
    });
    it("should have a property 'repeatInterval'", function () {
      expect(result).to.have.property("repeatInterval");
    });
    it("should have a property 'lastModifiedBy'", function () {
      expect(result).to.have.property("lastModifiedBy");
    });
    it("should have a property 'nextRunAt'", function () {
      expect(result).to.have.property("nextRunAt");
    });
    it("should return a date for the 'nextRunAt' property", function () {
      assert.typeOf(result.nextRunAt, "date");
    });
    it("should 'nextRunAt' to be a date after test date", function () {
      expect(result.nextRunAt).to.afterDate(testDate);
    });
  });
});

It's not it running before before finished, but this is ran before everything:

    client.connect(async function (err) {
     ...
    });

Mocha, when creates test suide runs code like this:

  • call each function that is passed to describe

    • collect all it s and before/after* hooks
    • that's when your client.connect snippet is actually called!
  • then, after whole suite/test tree is created, for each suite

    • call apropriate before* hooks
    • call all it s - tests, possibly surrounded by before/afterAll hooks
    • call after hooks

Each hook / test run waits for finish at least in your case, as you use async/await properly

Also, note that this is very fishy:

await setTimeout(() => {
  console.log('finished pausing for 5 seconds...');
}, 5000);

This doesn't wait for anything, because setTimeout returns timer it (number possibly) and it's not well defined what await should do with this data as this is not promise. For sure, it doesn't wait 5 seconds.

Proper promised wait looks as follows:

await new Promise(resolve => setTimeout(resolve, 5000))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM